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 8 import android.annotation.TargetApi; 9 import android.app.AlarmManager; 10 import android.app.AlarmManager.AlarmClockInfo; 11 import android.app.PendingIntent; 12 import android.content.Intent; 13 import java.util.ArrayList; 14 import java.util.Collections; 15 import java.util.List; 16 import org.robolectric.Shadows; 17 import org.robolectric.annotation.Implementation; 18 import org.robolectric.annotation.Implements; 19 20 @SuppressWarnings({"UnusedDeclaration"}) 21 @Implements(AlarmManager.class) 22 public class ShadowAlarmManager { 23 24 private final List<ScheduledAlarm> scheduledAlarms = new ArrayList<>(); 25 26 @Implementation 27 public void set(int type, long triggerAtTime, PendingIntent operation) { 28 internalSet(type, triggerAtTime, 0L, operation, null); 29 } 30 31 @Implementation(minSdk = KITKAT) 32 public void setExact(int type, long triggerAtTime, PendingIntent operation) { 33 internalSet(type, triggerAtTime, 0L, operation, null); 34 } 35 36 @Implementation(minSdk = KITKAT) 37 public void setWindow(int type, long windowStartMillis, long windowLengthMillis, 38 PendingIntent operation) { 39 internalSet(type, windowStartMillis, 0L, operation, null); 40 } 41 42 @Implementation(minSdk = M) 43 public void setAndAllowWhileIdle(int type, long triggerAtTime, PendingIntent operation) { 44 internalSet(type, triggerAtTime, 0L, operation, null); 45 } 46 47 @Implementation(minSdk = M) 48 public void setExactAndAllowWhileIdle(int type, long triggerAtTime, PendingIntent operation) { 49 internalSet(type, triggerAtTime, 0L, operation, null); 50 } 51 52 @Implementation 53 public void setRepeating(int type, long triggerAtTime, long interval, PendingIntent operation) { 54 internalSet(type, triggerAtTime, interval, operation, null); 55 } 56 57 @Implementation 58 public void setInexactRepeating(int type, long triggerAtMillis, long intervalMillis, 59 PendingIntent operation) { 60 internalSet(type, triggerAtMillis, intervalMillis, operation, null); 61 } 62 63 @Implementation(minSdk = LOLLIPOP) 64 public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) { 65 internalSet(RTC_WAKEUP, info.getTriggerTime(), 0L, operation, info.getShowIntent()); 66 } 67 68 @Implementation(minSdk = LOLLIPOP) 69 public AlarmClockInfo getNextAlarmClock() { 70 for (ScheduledAlarm scheduledAlarm : scheduledAlarms) { 71 AlarmClockInfo alarmClockInfo = scheduledAlarm.getAlarmClockInfo(); 72 if (alarmClockInfo != null) { 73 return alarmClockInfo; 74 } 75 } 76 return null; 77 } 78 79 private void internalSet(int type, long triggerAtTime, long interval, PendingIntent operation, 80 PendingIntent showIntent) { 81 cancel(operation); 82 scheduledAlarms.add(new ScheduledAlarm(type, triggerAtTime, interval, operation, showIntent)); 83 Collections.sort(scheduledAlarms); 84 } 85 86 /** 87 * @return the next scheduled alarm after consuming it 88 */ 89 public ScheduledAlarm getNextScheduledAlarm() { 90 if (scheduledAlarms.isEmpty()) { 91 return null; 92 } else { 93 return scheduledAlarms.remove(0); 94 } 95 } 96 97 /** 98 * @return the most recently scheduled alarm without consuming it 99 */ 100 public ScheduledAlarm peekNextScheduledAlarm() { 101 if (scheduledAlarms.isEmpty()) { 102 return null; 103 } else { 104 return scheduledAlarms.get(0); 105 } 106 } 107 108 /** 109 * @return all scheduled alarms 110 */ 111 public List<ScheduledAlarm> getScheduledAlarms() { 112 return scheduledAlarms; 113 } 114 115 @Implementation 116 public void cancel(PendingIntent operation) { 117 ShadowPendingIntent shadowPendingIntent = Shadows.shadowOf(operation); 118 final Intent toRemove = shadowPendingIntent.getSavedIntent(); 119 final int requestCode = shadowPendingIntent.getRequestCode(); 120 for (ScheduledAlarm scheduledAlarm : scheduledAlarms) { 121 ShadowPendingIntent scheduledShadowPendingIntent = Shadows.shadowOf(scheduledAlarm.operation); 122 final Intent scheduledIntent = scheduledShadowPendingIntent.getSavedIntent(); 123 final int scheduledRequestCode = scheduledShadowPendingIntent.getRequestCode(); 124 if (scheduledIntent.filterEquals(toRemove) && scheduledRequestCode == requestCode) { 125 scheduledAlarms.remove(scheduledAlarm); 126 break; 127 } 128 } 129 } 130 131 /** 132 * Container object to hold a PendingIntent and parameters describing when to send it. 133 */ 134 public static class ScheduledAlarm implements Comparable<ScheduledAlarm> { 135 136 public final int type; 137 public final long triggerAtTime; 138 public final long interval; 139 public final PendingIntent operation; 140 141 // A non-null showIntent implies this alarm has a user interface. (i.e. in an alarm clock app) 142 public final PendingIntent showIntent; 143 144 public ScheduledAlarm(int type, long triggerAtTime, PendingIntent operation, 145 PendingIntent showIntent) { 146 this(type, triggerAtTime, 0, operation, showIntent); 147 } 148 149 public ScheduledAlarm(int type, long triggerAtTime, long interval, PendingIntent operation, 150 PendingIntent showIntent) { 151 this.type = type; 152 this.triggerAtTime = triggerAtTime; 153 this.operation = operation; 154 this.interval = interval; 155 this.showIntent = showIntent; 156 } 157 158 @TargetApi(LOLLIPOP) 159 public AlarmClockInfo getAlarmClockInfo() { 160 return showIntent == null ? null : new AlarmClockInfo(triggerAtTime, showIntent); 161 } 162 163 @Override 164 public int compareTo(ScheduledAlarm scheduledAlarm) { 165 return Long.compare(triggerAtTime, scheduledAlarm.triggerAtTime); 166 } 167 } 168 } 169