• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.shadows;
2 
3 import static android.app.AlarmManager.RTC_WAKEUP;
4 import static android.os.Build.VERSION_CODES.KITKAT;
5 import static android.os.Build.VERSION_CODES.LOLLIPOP;
6 import static android.os.Build.VERSION_CODES.M;
7 import static android.os.Build.VERSION_CODES.N;
8 
9 import android.annotation.TargetApi;
10 import android.app.AlarmManager;
11 import android.app.AlarmManager.AlarmClockInfo;
12 import android.app.AlarmManager.OnAlarmListener;
13 import android.app.PendingIntent;
14 import android.content.Intent;
15 import android.os.Handler;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.List;
19 import java.util.TimeZone;
20 import org.robolectric.annotation.Implementation;
21 import org.robolectric.annotation.Implements;
22 import org.robolectric.annotation.RealObject;
23 import org.robolectric.annotation.Resetter;
24 import org.robolectric.shadow.api.Shadow;
25 
26 @SuppressWarnings({"UnusedDeclaration"})
27 @Implements(AlarmManager.class)
28 public class ShadowAlarmManager {
29 
30   private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getDefault();
31 
32   private final List<ScheduledAlarm> scheduledAlarms = new ArrayList<>();
33 
34   @RealObject private AlarmManager realObject;
35 
36   @Resetter
reset()37   public static void reset() {
38     TimeZone.setDefault(DEFAULT_TIMEZONE);
39   }
40 
41   @Implementation
setTimeZone(String timeZone)42   protected void setTimeZone(String timeZone) {
43     // Do the real check first
44     Shadow.directlyOn(realObject, AlarmManager.class).setTimeZone(timeZone);
45     // Then do the right side effect
46     TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
47   }
48 
49   @Implementation
set(int type, long triggerAtTime, PendingIntent operation)50   protected void set(int type, long triggerAtTime, PendingIntent operation) {
51     internalSet(type, triggerAtTime, 0L, operation, null);
52   }
53 
54   @Implementation(minSdk = N)
set( int type, long triggerAtTime, String tag, OnAlarmListener listener, Handler targetHandler)55   protected void set(
56       int type, long triggerAtTime, String tag, OnAlarmListener listener, Handler targetHandler) {
57     internalSet(type, triggerAtTime, listener, targetHandler);
58   }
59 
60   @Implementation(minSdk = KITKAT)
setExact(int type, long triggerAtTime, PendingIntent operation)61   protected void setExact(int type, long triggerAtTime, PendingIntent operation) {
62     internalSet(type, triggerAtTime, 0L, operation, null);
63   }
64 
65   @Implementation(minSdk = N)
setExact( int type, long triggerAtTime, String tag, OnAlarmListener listener, Handler targetHandler)66   protected void setExact(
67       int type, long triggerAtTime, String tag, OnAlarmListener listener, Handler targetHandler) {
68     internalSet(type, triggerAtTime, listener, targetHandler);
69   }
70 
71   @Implementation(minSdk = KITKAT)
setWindow( int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation)72   protected void setWindow(
73       int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation) {
74     internalSet(type, windowStartMillis, 0L, operation, null);
75   }
76 
77   @Implementation(minSdk = N)
setWindow( int type, long windowStartMillis, long windowLengthMillis, String tag, OnAlarmListener listener, Handler targetHandler)78   protected void setWindow(
79       int type,
80       long windowStartMillis,
81       long windowLengthMillis,
82       String tag,
83       OnAlarmListener listener,
84       Handler targetHandler) {
85     internalSet(type, windowStartMillis, listener, targetHandler);
86   }
87 
88   @Implementation(minSdk = M)
setAndAllowWhileIdle(int type, long triggerAtTime, PendingIntent operation)89   protected void setAndAllowWhileIdle(int type, long triggerAtTime, PendingIntent operation) {
90     internalSet(type, triggerAtTime, 0L, operation, null);
91   }
92 
93   @Implementation(minSdk = M)
setExactAndAllowWhileIdle(int type, long triggerAtTime, PendingIntent operation)94   protected void setExactAndAllowWhileIdle(int type, long triggerAtTime, PendingIntent operation) {
95     internalSet(type, triggerAtTime, 0L, operation, null);
96   }
97 
98   @Implementation
setRepeating( int type, long triggerAtTime, long interval, PendingIntent operation)99   protected void setRepeating(
100       int type, long triggerAtTime, long interval, PendingIntent operation) {
101     internalSet(type, triggerAtTime, interval, operation, null);
102   }
103 
104   @Implementation
setInexactRepeating( int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)105   protected void setInexactRepeating(
106       int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) {
107     internalSet(type, triggerAtMillis, intervalMillis, operation, null);
108   }
109 
110   @Implementation(minSdk = LOLLIPOP)
setAlarmClock(AlarmClockInfo info, PendingIntent operation)111   protected void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
112     internalSet(RTC_WAKEUP, info.getTriggerTime(), 0L, operation, info.getShowIntent());
113   }
114 
115   @Implementation(minSdk = LOLLIPOP)
getNextAlarmClock()116   protected AlarmClockInfo getNextAlarmClock() {
117     for (ScheduledAlarm scheduledAlarm : scheduledAlarms) {
118       AlarmClockInfo alarmClockInfo = scheduledAlarm.getAlarmClockInfo();
119       if (alarmClockInfo != null) {
120         return alarmClockInfo;
121       }
122     }
123     return null;
124   }
125 
internalSet(int type, long triggerAtTime, long interval, PendingIntent operation, PendingIntent showIntent)126   private void internalSet(int type, long triggerAtTime, long interval, PendingIntent operation,
127       PendingIntent showIntent) {
128     cancel(operation);
129     scheduledAlarms.add(new ScheduledAlarm(type, triggerAtTime, interval, operation, showIntent));
130     Collections.sort(scheduledAlarms);
131   }
132 
internalSet( int type, long triggerAtTime, OnAlarmListener listener, Handler handler)133   private void internalSet(
134       int type, long triggerAtTime, OnAlarmListener listener, Handler handler) {
135     cancel(listener);
136     scheduledAlarms.add(new ScheduledAlarm(type, triggerAtTime, 0L, listener, handler));
137     Collections.sort(scheduledAlarms);
138   }
139 
140   /**
141    * @return the next scheduled alarm after consuming it
142    */
getNextScheduledAlarm()143   public ScheduledAlarm getNextScheduledAlarm() {
144     if (scheduledAlarms.isEmpty()) {
145       return null;
146     } else {
147       return scheduledAlarms.remove(0);
148     }
149   }
150 
151   /**
152    * @return the most recently scheduled alarm without consuming it
153    */
peekNextScheduledAlarm()154   public ScheduledAlarm peekNextScheduledAlarm() {
155     if (scheduledAlarms.isEmpty()) {
156       return null;
157     } else {
158       return scheduledAlarms.get(0);
159     }
160   }
161 
162   /**
163    * @return all scheduled alarms
164    */
getScheduledAlarms()165   public List<ScheduledAlarm> getScheduledAlarms() {
166     return scheduledAlarms;
167   }
168 
169   @Implementation
cancel(PendingIntent operation)170   protected void cancel(PendingIntent operation) {
171     ShadowPendingIntent shadowPendingIntent = Shadow.extract(operation);
172     final Intent toRemove = shadowPendingIntent.getSavedIntent();
173     final int requestCode = shadowPendingIntent.getRequestCode();
174     for (ScheduledAlarm scheduledAlarm : scheduledAlarms) {
175       if (scheduledAlarm.operation != null) {
176         ShadowPendingIntent scheduledShadowPendingIntent = Shadow.extract(scheduledAlarm.operation);
177         final Intent scheduledIntent = scheduledShadowPendingIntent.getSavedIntent();
178         final int scheduledRequestCode = scheduledShadowPendingIntent.getRequestCode();
179         if (scheduledIntent.filterEquals(toRemove) && scheduledRequestCode == requestCode) {
180           scheduledAlarms.remove(scheduledAlarm);
181           break;
182         }
183       }
184     }
185   }
186 
187   @Implementation(minSdk = N)
cancel(OnAlarmListener listener)188   protected void cancel(OnAlarmListener listener) {
189     for (ScheduledAlarm scheduledAlarm : scheduledAlarms) {
190       if (scheduledAlarm.onAlarmListener != null) {
191         if (scheduledAlarm.onAlarmListener.equals(listener)) {
192           scheduledAlarms.remove(scheduledAlarm);
193           break;
194         }
195       }
196     }
197   }
198 
199   /**
200    * Container object to hold a PendingIntent and parameters describing when to send it.
201    */
202   public static class ScheduledAlarm implements Comparable<ScheduledAlarm> {
203 
204     public final int type;
205     public final long triggerAtTime;
206     public final long interval;
207     public final PendingIntent operation;
208 
209     // A non-null showIntent implies this alarm has a user interface. (i.e. in an alarm clock app)
210     public final PendingIntent showIntent;
211 
212     public final OnAlarmListener onAlarmListener;
213     public final Handler handler;
214 
ScheduledAlarm(int type, long triggerAtTime, PendingIntent operation, PendingIntent showIntent)215     public ScheduledAlarm(int type, long triggerAtTime, PendingIntent operation,
216         PendingIntent showIntent) {
217       this(type, triggerAtTime, 0, operation, showIntent);
218     }
219 
ScheduledAlarm(int type, long triggerAtTime, long interval, PendingIntent operation, PendingIntent showIntent)220     public ScheduledAlarm(int type, long triggerAtTime, long interval, PendingIntent operation,
221         PendingIntent showIntent) {
222       this(type, triggerAtTime, interval, operation, showIntent, null, null);
223     }
224 
ScheduledAlarm( int type, long triggerAtTime, long interval, OnAlarmListener onAlarmListener, Handler handler)225     private ScheduledAlarm(
226         int type,
227         long triggerAtTime,
228         long interval,
229         OnAlarmListener onAlarmListener,
230         Handler handler) {
231       this(type, triggerAtTime, interval, null, null, onAlarmListener, handler);
232     }
233 
ScheduledAlarm( int type, long triggerAtTime, long interval, PendingIntent operation, PendingIntent showIntent, OnAlarmListener onAlarmListener, Handler handler)234     private ScheduledAlarm(
235         int type,
236         long triggerAtTime,
237         long interval,
238         PendingIntent operation,
239         PendingIntent showIntent,
240         OnAlarmListener onAlarmListener,
241         Handler handler) {
242       this.type = type;
243       this.triggerAtTime = triggerAtTime;
244       this.operation = operation;
245       this.interval = interval;
246       this.showIntent = showIntent;
247       this.onAlarmListener = onAlarmListener;
248       this.handler = handler;
249     }
250 
251     @TargetApi(LOLLIPOP)
getAlarmClockInfo()252     public AlarmClockInfo getAlarmClockInfo() {
253       return showIntent == null ? null : new AlarmClockInfo(triggerAtTime, showIntent);
254     }
255 
256     @Override
compareTo(ScheduledAlarm scheduledAlarm)257     public int compareTo(ScheduledAlarm scheduledAlarm) {
258       return Long.compare(triggerAtTime, scheduledAlarm.triggerAtTime);
259     }
260   }
261 }
262