1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.core.app;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.os.Build;
22
23import androidx.annotation.NonNull;
24
25/**
26 * Compatibility library for {@link AlarmManager} with fallbacks for older platforms.
27 */
28public final class AlarmManagerCompat {
29    /**
30     * Schedule an alarm that represents an alarm clock.
31     *
32     * The system may choose to display information about this alarm to the user.
33     *
34     * <p>
35     * This method is like {@link #setExact}, but implies
36     * {@link AlarmManager#RTC_WAKEUP}.
37     *
38     * @param alarmManager AlarmManager instance used to set the alarm
39     * @param triggerTime time at which the underlying alarm is triggered in wall time
40     *                    milliseconds since the epoch
41     * @param showIntent an intent that can be used to show or edit details of
42     *                    the alarm clock.
43     * @param operation Action to perform when the alarm goes off;
44     *        typically comes from {@link PendingIntent#getBroadcast
45     *        IntentSender.getBroadcast()}.
46     *
47     * @see AlarmManager#set
48     * @see AlarmManager#setRepeating
49     * @see AlarmManager#setWindow
50     * @see #setExact
51     * @see AlarmManager#cancel
52     * @see AlarmManager#getNextAlarmClock()
53     * @see android.content.Context#sendBroadcast
54     * @see android.content.Context#registerReceiver
55     * @see android.content.Intent#filterEquals
56     */
57    public static void setAlarmClock(@NonNull AlarmManager alarmManager, long triggerTime,
58            @NonNull PendingIntent showIntent, @NonNull PendingIntent operation) {
59        if (Build.VERSION.SDK_INT >= 21) {
60            alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(triggerTime, showIntent),
61                    operation);
62        } else {
63            AlarmManagerCompat.setExact(alarmManager, AlarmManager.RTC_WAKEUP, triggerTime,
64                    operation);
65        }
66    }
67
68    /**
69     * Like {@link AlarmManager#set(int, long, PendingIntent)}, but this alarm will be allowed to
70     * execute even when the system is in low-power idle modes.  This type of alarm must <b>only</b>
71     * be used for situations where it is actually required that the alarm go off while in
72     * idle -- a reasonable example would be for a calendar notification that should make a
73     * sound so the user is aware of it.  When the alarm is dispatched, the app will also be
74     * added to the system's temporary whitelist for approximately 10 seconds to allow that
75     * application to acquire further wake locks in which to complete its work.</p>
76     *
77     * <p>These alarms can significantly impact the power use
78     * of the device when idle (and thus cause significant battery blame to the app scheduling
79     * them), so they should be used with care.  To reduce abuse, there are restrictions on how
80     * frequently these alarms will go off for a particular application.
81     * Under normal system operation, it will not dispatch these
82     * alarms more than about every minute (at which point every such pending alarm is
83     * dispatched); when in low-power idle modes this duration may be significantly longer,
84     * such as 15 minutes.</p>
85     *
86     * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen
87     * out of order with any other alarms, even those from the same app.  This will clearly happen
88     * when the device is idle (since this alarm can go off while idle, when any other alarms
89     * from the app will be held until later), but may also happen even when not idle.</p>
90     *
91     * <p>Regardless of the app's target SDK version, this call always allows batching of the
92     * alarm.</p>
93     *
94     * @param alarmManager AlarmManager instance used to set the alarm
95     * @param type One of {@link AlarmManager#ELAPSED_REALTIME},
96     *        {@link AlarmManager#ELAPSED_REALTIME_WAKEUP},
97     *        {@link AlarmManager#RTC}, or {@link AlarmManager#RTC_WAKEUP}.
98     * @param triggerAtMillis time in milliseconds that the alarm should go
99     * off, using the appropriate clock (depending on the alarm type).
100     * @param operation Action to perform when the alarm goes off;
101     * typically comes from {@link PendingIntent#getBroadcast
102     * IntentSender.getBroadcast()}.
103     *
104     * @see AlarmManager#set(int, long, PendingIntent)
105     * @see #setExactAndAllowWhileIdle
106     * @see AlarmManager#cancel
107     * @see android.content.Context#sendBroadcast
108     * @see android.content.Context#registerReceiver
109     * @see android.content.Intent#filterEquals
110     * @see AlarmManager#ELAPSED_REALTIME
111     * @see AlarmManager#ELAPSED_REALTIME_WAKEUP
112     * @see AlarmManager#RTC
113     * @see AlarmManager#RTC_WAKEUP
114     */
115    public static void setAndAllowWhileIdle(@NonNull AlarmManager alarmManager, int type,
116            long triggerAtMillis, @NonNull PendingIntent operation) {
117        if (Build.VERSION.SDK_INT >= 23) {
118            alarmManager.setAndAllowWhileIdle(type, triggerAtMillis, operation);
119        } else {
120            alarmManager.set(type, triggerAtMillis, operation);
121        }
122    }
123
124    /**
125     * Schedule an alarm to be delivered precisely at the stated time.
126     *
127     * <p>
128     * This method is like {@link AlarmManager#set(int, long, PendingIntent)}, but does not permit
129     * the OS to adjust the delivery time.  The alarm will be delivered as nearly as
130     * possible to the requested trigger time.
131     *
132     * <p>
133     * <b>Note:</b> only alarms for which there is a strong demand for exact-time
134     * delivery (such as an alarm clock ringing at the requested time) should be
135     * scheduled as exact.  Applications are strongly discouraged from using exact
136     * alarms unnecessarily as they reduce the OS's ability to minimize battery use.
137     *
138     * @param alarmManager AlarmManager instance used to set the alarm
139     * @param type One of {@link AlarmManager#ELAPSED_REALTIME},
140     *        {@link AlarmManager#ELAPSED_REALTIME_WAKEUP},
141     *        {@link AlarmManager#RTC}, or {@link AlarmManager#RTC_WAKEUP}.
142     * @param triggerAtMillis time in milliseconds that the alarm should go
143     *        off, using the appropriate clock (depending on the alarm type).
144     * @param operation Action to perform when the alarm goes off;
145     *        typically comes from {@link PendingIntent#getBroadcast
146     *        IntentSender.getBroadcast()}.
147     *
148     * @see AlarmManager#set
149     * @see AlarmManager#setRepeating
150     * @see AlarmManager#setWindow
151     * @see AlarmManager#cancel
152     * @see android.content.Context#sendBroadcast
153     * @see android.content.Context#registerReceiver
154     * @see android.content.Intent#filterEquals
155     * @see AlarmManager#ELAPSED_REALTIME
156     * @see AlarmManager#ELAPSED_REALTIME_WAKEUP
157     * @see AlarmManager#RTC
158     * @see AlarmManager#RTC_WAKEUP
159     */
160    public static void setExact(@NonNull AlarmManager alarmManager, int type, long triggerAtMillis,
161            @NonNull PendingIntent operation) {
162        if (Build.VERSION.SDK_INT >= 19) {
163            alarmManager.setExact(type, triggerAtMillis, operation);
164        } else {
165            alarmManager.set(type, triggerAtMillis, operation);
166        }
167    }
168
169    /**
170     * Like {@link #setExact}, but this alarm will be allowed to execute
171     * even when the system is in low-power idle modes.  If you don't need exact scheduling of
172     * the alarm but still need to execute while idle, consider using
173     * {@link #setAndAllowWhileIdle}.  This type of alarm must <b>only</b>
174     * be used for situations where it is actually required that the alarm go off while in
175     * idle -- a reasonable example would be for a calendar notification that should make a
176     * sound so the user is aware of it.  When the alarm is dispatched, the app will also be
177     * added to the system's temporary whitelist for approximately 10 seconds to allow that
178     * application to acquire further wake locks in which to complete its work.</p>
179     *
180     * <p>These alarms can significantly impact the power use
181     * of the device when idle (and thus cause significant battery blame to the app scheduling
182     * them), so they should be used with care.  To reduce abuse, there are restrictions on how
183     * frequently these alarms will go off for a particular application.
184     * Under normal system operation, it will not dispatch these
185     * alarms more than about every minute (at which point every such pending alarm is
186     * dispatched); when in low-power idle modes this duration may be significantly longer,
187     * such as 15 minutes.</p>
188     *
189     * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen
190     * out of order with any other alarms, even those from the same app.  This will clearly happen
191     * when the device is idle (since this alarm can go off while idle, when any other alarms
192     * from the app will be held until later), but may also happen even when not idle.
193     * Note that the OS will allow itself more flexibility for scheduling these alarms than
194     * regular exact alarms, since the application has opted into this behavior.  When the
195     * device is idle it may take even more liberties with scheduling in order to optimize
196     * for battery life.</p>
197     *
198     * @param alarmManager AlarmManager instance used to set the alarm
199     * @param type One of {@link AlarmManager#ELAPSED_REALTIME},
200     *        {@link AlarmManager#ELAPSED_REALTIME_WAKEUP},
201     *        {@link AlarmManager#RTC}, or {@link AlarmManager#RTC_WAKEUP}.
202     * @param triggerAtMillis time in milliseconds that the alarm should go
203     *        off, using the appropriate clock (depending on the alarm type).
204     * @param operation Action to perform when the alarm goes off;
205     *        typically comes from {@link PendingIntent#getBroadcast
206     *        IntentSender.getBroadcast()}.
207     *
208     * @see AlarmManager#set
209     * @see AlarmManager#setRepeating
210     * @see AlarmManager#setWindow
211     * @see AlarmManager#cancel
212     * @see android.content.Context#sendBroadcast
213     * @see android.content.Context#registerReceiver
214     * @see android.content.Intent#filterEquals
215     * @see AlarmManager#ELAPSED_REALTIME
216     * @see AlarmManager#ELAPSED_REALTIME_WAKEUP
217     * @see AlarmManager#RTC
218     * @see AlarmManager#RTC_WAKEUP
219     */
220    public static void setExactAndAllowWhileIdle(@NonNull AlarmManager alarmManager, int type,
221            long triggerAtMillis, @NonNull PendingIntent operation) {
222        if (Build.VERSION.SDK_INT >= 23) {
223            alarmManager.setExactAndAllowWhileIdle(type, triggerAtMillis, operation);
224        } else {
225            AlarmManagerCompat.setExact(alarmManager, type, triggerAtMillis, operation);
226        }
227    }
228
229    private AlarmManagerCompat() {
230    }
231}
232