15fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project/* 25fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project 35fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * 45fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 55fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * you may not use this file except in compliance with the License. 65fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * You may obtain a copy of the License at 75fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * 85fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 95fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * 105fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 115fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 125fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * See the License for the specific language governing permissions and 145fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * limitations under the License. 155fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 165fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 17fa954248b41b567dfa2ddd7bd063d3936a51758eDaniel Sandlerpackage com.android.deskclock; 185fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 195fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectimport android.app.AlarmManager; 20c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scottimport android.app.NotificationManager; 215fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectimport android.app.PendingIntent; 225fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectimport android.content.ContentResolver; 235fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectimport android.content.ContentValues; 245fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectimport android.content.ContentUris; 255fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectimport android.content.Context; 265fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectimport android.content.Intent; 275fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectimport android.content.SharedPreferences; 285fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectimport android.database.Cursor; 295fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectimport android.net.Uri; 30d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scottimport android.os.Parcel; 315fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectimport android.provider.Settings; 325fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectimport android.text.format.DateFormat; 335fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 345fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectimport java.util.Calendar; 358304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrookimport java.util.HashSet; 368304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrookimport java.util.Set; 375fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 385fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project/** 395fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * The Alarms provider supplies info about Alarm Clock settings 405fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 415fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectpublic class Alarms { 425fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 43d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // This action triggers the AlarmReceiver as well as the AlarmKlaxon. It 44d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // is a public action used in the manifest for receiving Alarm broadcasts 45d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // from the alarm manager. 46fa954248b41b567dfa2ddd7bd063d3936a51758eDaniel Sandler public static final String ALARM_ALERT_ACTION = "com.android.deskclock.ALARM_ALERT"; 475fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 48a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram // A public action sent by AlarmKlaxon when the alarm has stopped sounding 49a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram // for any reason (e.g. because it has been dismissed from AlarmAlertFullScreen, 50a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram // or killed due to an incoming phone call, etc). 51a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram public static final String ALARM_DONE_ACTION = "com.android.deskclock.ALARM_DONE"; 52a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram 53a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram // AlarmAlertFullScreen listens for this broadcast intent, so that other applications 54a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram // can snooze the alarm (after ALARM_ALERT_ACTION and before ALARM_DONE_ACTION). 55a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram public static final String ALARM_SNOOZE_ACTION = "com.android.deskclock.ALARM_SNOOZE"; 56a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram 57a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram // AlarmAlertFullScreen listens for this broadcast intent, so that other applications 58a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram // can dismiss the alarm (after ALARM_ALERT_ACTION and before ALARM_DONE_ACTION). 59a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram public static final String ALARM_DISMISS_ACTION = "com.android.deskclock.ALARM_DISMISS"; 60a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram 61d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // This is a private action used by the AlarmKlaxon to update the UI to 62d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // show the alarm has been killed. 63d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott public static final String ALARM_KILLED = "alarm_killed"; 645fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 65d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // Extra in the ALARM_KILLED intent to indicate to the user how long the 66d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // alarm played before being killed. 67d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott public static final String ALARM_KILLED_TIMEOUT = "alarm_killed_timeout"; 685fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 69d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // This string is used to indicate a silent alarm in the db. 70d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott public static final String ALARM_ALERT_SILENT = "silent"; 715fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 72d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // This intent is sent from the notification when the user cancels the 73d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // snooze alert. 74d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott public static final String CANCEL_SNOOZE = "cancel_snooze"; 755fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 76d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // This string is used when passing an Alarm object through an intent. 77d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott public static final String ALARM_INTENT_EXTRA = "intent.extra.alarm"; 785fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 79d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // This extra is the raw Alarm object data. It is used in the 80d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // AlarmManagerService to avoid a ClassNotFoundException when filling in 81d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // the Intent extras. 82d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott public static final String ALARM_RAW_DATA = "intent.extra.alarm_raw"; 835fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 848304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook private static final String PREF_SNOOZE_IDS = "snooze_ids"; 858304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook private static final String PREF_SNOOZE_TIME = "snooze_time"; 865fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 87d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott private final static String DM12 = "E h:mm aa"; 88bababb66c17c665d374313da47e2f3734adec83aPatrick Scott private final static String DM24 = "E kk:mm"; 895fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 90d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott private final static String M12 = "h:mm aa"; 911ebd23eaa82ab560f5c7d8aeca8df7512df8424ePatrick Scott // Shared with DigitalClock 921ebd23eaa82ab560f5c7d8aeca8df7512df8424ePatrick Scott final static String M24 = "kk:mm"; 935fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 948304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final static int INVALID_ALARM_ID = -1; 958304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 965fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 976064d3f7aabcb073dc3f917e049bc04c67fb6b29Patrick Scott * Creates a new Alarm and fills in the given alarm's id. 985fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 99e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott public static long addAlarm(Context context, Alarm alarm) { 100e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott ContentValues values = createContentValues(alarm); 1016064d3f7aabcb073dc3f917e049bc04c67fb6b29Patrick Scott Uri uri = context.getContentResolver().insert( 1026064d3f7aabcb073dc3f917e049bc04c67fb6b29Patrick Scott Alarm.Columns.CONTENT_URI, values); 1036064d3f7aabcb073dc3f917e049bc04c67fb6b29Patrick Scott alarm.id = (int) ContentUris.parseId(uri); 104e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott 105e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott long timeInMillis = calculateAlarm(alarm); 106e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott if (alarm.enabled) { 107e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott clearSnoozeIfNeeded(context, timeInMillis); 108e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott } 109e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott setNextAlert(context); 110e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott return timeInMillis; 1115fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 1125fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 1135fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 1145fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * Removes an existing Alarm. If this alarm is snoozing, disables 1155fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * snooze. Sets next alert. 1165fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 117bb13b216f986fc75dbd82d38f2bd6b2fbeb08abdPatrick Scott public static void deleteAlarm(Context context, int alarmId) { 1188304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook if (alarmId == INVALID_ALARM_ID) return; 1195fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 1205fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project ContentResolver contentResolver = context.getContentResolver(); 1215fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /* If alarm is snoozing, lose it */ 122d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott disableSnoozeAlert(context, alarmId); 1235fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 124d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott Uri uri = ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarmId); 125d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott contentResolver.delete(uri, "", null); 1265fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 1275fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project setNextAlert(context); 1285fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 1295fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 1305fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 1315fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * Queries all alarms 1325fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * @return cursor over all alarms 1335fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 134d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott public static Cursor getAlarmsCursor(ContentResolver contentResolver) { 1355fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project return contentResolver.query( 136d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott Alarm.Columns.CONTENT_URI, Alarm.Columns.ALARM_QUERY_COLUMNS, 137d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott null, null, Alarm.Columns.DEFAULT_SORT_ORDER); 1385fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 1395fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 140d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // Private method to get a more limited set of alarms from the database. 141d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott private static Cursor getFilteredAlarmsCursor( 142d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott ContentResolver contentResolver) { 143d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott return contentResolver.query(Alarm.Columns.CONTENT_URI, 144d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott Alarm.Columns.ALARM_QUERY_COLUMNS, Alarm.Columns.WHERE_ENABLED, 145d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott null, null); 1465fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 1475fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 148e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott private static ContentValues createContentValues(Alarm alarm) { 149e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott ContentValues values = new ContentValues(8); 150e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott // Set the alarm_time value if this alarm does not repeat. This will be 151e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott // used later to disable expire alarms. 152e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott long time = 0; 153e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott if (!alarm.daysOfWeek.isRepeatSet()) { 154e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott time = calculateAlarm(alarm); 155e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott } 156e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott 157e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott values.put(Alarm.Columns.ENABLED, alarm.enabled ? 1 : 0); 158e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott values.put(Alarm.Columns.HOUR, alarm.hour); 159e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott values.put(Alarm.Columns.MINUTES, alarm.minutes); 160501a153616bd44880b587447788990d83f433a8fPatrick Scott values.put(Alarm.Columns.ALARM_TIME, time); 161e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott values.put(Alarm.Columns.DAYS_OF_WEEK, alarm.daysOfWeek.getCoded()); 162e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott values.put(Alarm.Columns.VIBRATE, alarm.vibrate); 163e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott values.put(Alarm.Columns.MESSAGE, alarm.label); 164e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott 165e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott // A null alert Uri indicates a silent alarm. 166e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott values.put(Alarm.Columns.ALERT, alarm.alert == null ? ALARM_ALERT_SILENT 167e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott : alarm.alert.toString()); 168e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott 169e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott return values; 170e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott } 171e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott 172e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott private static void clearSnoozeIfNeeded(Context context, long alarmTime) { 173e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott // If this alarm fires before the next snooze, clear the snooze to 174e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott // enable this alarm. 1758304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook SharedPreferences prefs = context.getSharedPreferences(AlarmClock.PREFERENCES, 0); 1768304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 1778304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook // Get the list of snoozed alarms 1788304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final Set<String> snoozedIds = prefs.getStringSet(PREF_SNOOZE_IDS, new HashSet<String>()); 1798304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook for (String snoozedAlarm : snoozedIds) { 1808304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final long snoozeTime = prefs.getLong(getAlarmPrefSnoozeTimeKey(snoozedAlarm), 0); 1818304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook if (alarmTime < snoozeTime) { 1828304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final int alarmId = Integer.parseInt(snoozedAlarm); 1838304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook clearSnoozePreference(context, prefs, alarmId); 1848304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } 185e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott } 186e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott } 187e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott 1885fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 189d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott * Return an Alarm object representing the alarm id in the database. 190d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott * Returns null if no alarm exists. 1915fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 192d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott public static Alarm getAlarm(ContentResolver contentResolver, int alarmId) { 1935fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project Cursor cursor = contentResolver.query( 194d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarmId), 195d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott Alarm.Columns.ALARM_QUERY_COLUMNS, 196d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott null, null, null); 197d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott Alarm alarm = null; 198d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott if (cursor != null) { 199d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott if (cursor.moveToFirst()) { 200d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott alarm = new Alarm(cursor); 201d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott } 202d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott cursor.close(); 203d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott } 204d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott return alarm; 2055fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 2065fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 2075fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 2085fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 2095fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * A convenience method to set an alarm in the Alarms 2105fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * content provider. 211c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott * @return Time when the alarm will fire. 2125fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 213e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott public static long setAlarm(Context context, Alarm alarm) { 214e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott ContentValues values = createContentValues(alarm); 2155fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project ContentResolver resolver = context.getContentResolver(); 216e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott resolver.update( 217e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarm.id), 218e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott values, null, null); 2195fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 220e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott long timeInMillis = calculateAlarm(alarm); 221c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott 222e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott if (alarm.enabled) { 223232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott // Disable the snooze if we just changed the snoozed alarm. This 224232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott // only does work if the snoozed alarm is the same as the given 225232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott // alarm. 226232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott // TODO: disableSnoozeAlert should have a better name. 227232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott disableSnoozeAlert(context, alarm.id); 228232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott 229232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott // Disable the snooze if this alarm fires before the snoozed alarm. 230232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott // This works on every alarm since the user most likely intends to 231232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott // have the modified alarm fire next. 232e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott clearSnoozeIfNeeded(context, timeInMillis); 233c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott } 234c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott 2355fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project setNextAlert(context); 236c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott 237c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott return timeInMillis; 2385fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 2395fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 2405fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 2415fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * A convenience method to enable or disable an alarm. 2425fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * 2435fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * @param id corresponds to the _id column 2445fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * @param enabled corresponds to the ENABLED column 2455fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 2465fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 247d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott public static void enableAlarm( 2485fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project final Context context, final int id, boolean enabled) { 2495fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project enableAlarmInternal(context, id, enabled); 2505fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project setNextAlert(context); 2515fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 2525fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 253d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott private static void enableAlarmInternal(final Context context, 254d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott final int id, boolean enabled) { 255d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott enableAlarmInternal(context, getAlarm(context.getContentResolver(), id), 256d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott enabled); 257d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott } 2585fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 259d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott private static void enableAlarmInternal(final Context context, 260d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott final Alarm alarm, boolean enabled) { 26122924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott if (alarm == null) { 26222924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott return; 26322924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott } 264d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott ContentResolver resolver = context.getContentResolver(); 2655fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 2665fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project ContentValues values = new ContentValues(2); 267d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott values.put(Alarm.Columns.ENABLED, enabled ? 1 : 0); 2685fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 269d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // If we are enabling the alarm, calculate alarm time since the time 270d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // value in Alarm may be old. 2715fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project if (enabled) { 272d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott long time = 0; 273d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott if (!alarm.daysOfWeek.isRepeatSet()) { 274e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott time = calculateAlarm(alarm); 2755fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 276d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott values.put(Alarm.Columns.ALARM_TIME, time); 27722924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott } else { 27822924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott // Clear the snooze if the id matches. 27922924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott disableSnoozeAlert(context, alarm.id); 2805fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 2815fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 282d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott resolver.update(ContentUris.withAppendedId( 283d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott Alarm.Columns.CONTENT_URI, alarm.id), values, null, null); 2845fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 2855fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 2868304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook private static Alarm calculateNextAlert(final Context context) { 287d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott long minTime = Long.MAX_VALUE; 28898948f9ce1dabcc79350a847baf220635b0c087aPatrick Scott long now = System.currentTimeMillis(); 2898304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final SharedPreferences prefs = context.getSharedPreferences(AlarmClock.PREFERENCES, 0); 2908304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 2918304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook Set<Alarm> alarms = new HashSet<Alarm>(); 2928304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 2938304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook // We need to to build the list of alarms from both the snoozed list and the scheduled 2948304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook // list. For a non-repeating alarm, when it goes of, it becomes disabled. A snoozed 2958304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook // non-repeating alarm is not in the active list in the database. 2968304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 2978304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook // first go through the snoozed alarms 2988304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final Set<String> snoozedIds = prefs.getStringSet(PREF_SNOOZE_IDS, new HashSet<String>()); 2998304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook for (String snoozedAlarm : snoozedIds) { 3008304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final int alarmId = Integer.parseInt(snoozedAlarm); 3018304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final Alarm a = getAlarm(context.getContentResolver(), alarmId); 3028304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook alarms.add(a); 3038304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } 3048304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 3058304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook // Now add the scheduled alarms 3068304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final Cursor cursor = getFilteredAlarmsCursor(context.getContentResolver()); 307d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott if (cursor != null) { 30899ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden try { 30999ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden if (cursor.moveToFirst()) { 3108304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook do { 3118304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final Alarm a = new Alarm(cursor); 3128304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook alarms.add(a); 3138304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } while (cursor.moveToNext()); 3148304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } 31599ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden } finally { 31699ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden cursor.close(); 3175fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 3185fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 3198304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 3208304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook Alarm alarm = null; 3218304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 3228304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook for (Alarm a : alarms) { 3238304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook // A time of 0 indicates this is a repeating alarm, so 3248304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook // calculate the time to get the next alert. 3258304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook if (a.time == 0) { 3268304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook a.time = calculateAlarm(a); 3278304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } 3288304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 3298304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook // Update the alarm if it has been snoozed 3308304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook updateAlarmTimeForSnooze(prefs, a); 3318304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 3328304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook if (a.time < now) { 3338304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook Log.v("Disabling expired alarm set for " + Log.formatTime(a.time)); 3348304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook // Expired alarm, disable it and move along. 3358304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook enableAlarmInternal(context, a, false); 3368304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook continue; 3378304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } 3388304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook if (a.time < minTime) { 3398304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook minTime = a.time; 3408304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook alarm = a; 3418304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } 3428304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } 3438304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 344d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott return alarm; 3455fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 3465fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 3475fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 3485fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * Disables non-repeating alarms that have passed. Called at 3495fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * boot. 3505fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 3515fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project public static void disableExpiredAlarms(final Context context) { 352d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott Cursor cur = getFilteredAlarmsCursor(context.getContentResolver()); 3535fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project long now = System.currentTimeMillis(); 3545fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 35599ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden try { 35699ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden if (cur.moveToFirst()) { 35799ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden do { 35899ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden Alarm alarm = new Alarm(cur); 35999ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden // A time of 0 means this alarm repeats. If the time is 36099ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden // non-zero, check if the time is before now. 36199ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden if (alarm.time != 0 && alarm.time < now) { 36299ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden Log.v("Disabling expired alarm set for " + 36399ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden Log.formatTime(alarm.time)); 36499ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden enableAlarmInternal(context, alarm, false); 36599ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden } 36699ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden } while (cur.moveToNext()); 36799ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden } 36899ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden } finally { 36999ae7660f1e5e4645c889e78e26d44de20a7caf3Andy McFadden cur.close(); 3705fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 3715fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 3725fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 3735fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 3745fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * Called at system startup, on time/timezone change, and whenever 3755fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * the user changes alarm settings. Activates snooze if set, 3765fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * otherwise loads all alarms, activates next alert. 3775fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 3785fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project public static void setNextAlert(final Context context) { 3798304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final Alarm alarm = calculateNextAlert(context); 3808304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook if (alarm != null) { 3818304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook enableAlert(context, alarm, alarm.time); 3828304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } else { 3838304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook disableAlert(context); 3845fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 3855fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 3865fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 3875fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 3885fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * Sets alert in AlarmManger and StatusBar. This is what will 3895fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * actually launch the alert when the alarm triggers. 3905fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * 391d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott * @param alarm Alarm. 3925fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * @param atTimeInMillis milliseconds since epoch 3935fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 394d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott private static void enableAlert(Context context, final Alarm alarm, 395d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott final long atTimeInMillis) { 3965fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project AlarmManager am = (AlarmManager) 3975fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project context.getSystemService(Context.ALARM_SERVICE); 3985fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 399d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott if (Log.LOGV) { 400d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott Log.v("** setAlert id " + alarm.id + " atTime " + atTimeInMillis); 4015d6f5fa51fa7fd13784a73199161deabec5bc898Patrick Scott } 402d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott 403d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott Intent intent = new Intent(ALARM_ALERT_ACTION); 404d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott 405d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // XXX: This is a slight hack to avoid an exception in the remote 406d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // AlarmManagerService process. The AlarmManager adds extra data to 407d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // this Intent which causes it to inflate. Since the remote process 408d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // does not know about the Alarm class, it throws a 409d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // ClassNotFoundException. 410d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // 411d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // To avoid this, we marshall the data ourselves and then parcel a plain 412d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // byte[] array. The AlarmReceiver class knows to build the Alarm 413d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // object from the byte[] array. 414d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott Parcel out = Parcel.obtain(); 415d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott alarm.writeToParcel(out, 0); 416d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott out.setDataPosition(0); 417d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott intent.putExtra(ALARM_RAW_DATA, out.marshall()); 418d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott 4195fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project PendingIntent sender = PendingIntent.getBroadcast( 4205fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 4215fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 422d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender); 4235fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 4245fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project setStatusBarIcon(context, true); 4255fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 4265fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project Calendar c = Calendar.getInstance(); 427e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott c.setTimeInMillis(atTimeInMillis); 4285fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project String timeString = formatDayAndTime(context, c); 4295fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project saveNextAlarm(context, timeString); 4305fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 4315fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 4325fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 4335fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * Disables alert in AlarmManger and StatusBar. 4345fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * 4355fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * @param id Alarm ID. 4365fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 437d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott static void disableAlert(Context context) { 4385fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project AlarmManager am = (AlarmManager) 4395fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project context.getSystemService(Context.ALARM_SERVICE); 4405fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project PendingIntent sender = PendingIntent.getBroadcast( 441d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott context, 0, new Intent(ALARM_ALERT_ACTION), 442d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott PendingIntent.FLAG_CANCEL_CURRENT); 4435fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project am.cancel(sender); 4445fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project setStatusBarIcon(context, false); 4455fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project saveNextAlarm(context, ""); 4465fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 4475fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 448d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott static void saveSnoozeAlert(final Context context, final int id, 449d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott final long time) { 4505fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project SharedPreferences prefs = context.getSharedPreferences( 4515fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project AlarmClock.PREFERENCES, 0); 4528304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook if (id == INVALID_ALARM_ID) { 4538304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook clearAllSnoozePreferences(context, prefs); 454d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott } else { 4558304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final Set<String> snoozedIds = 4568304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook prefs.getStringSet(PREF_SNOOZE_IDS, new HashSet<String>()); 4578304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook snoozedIds.add(Integer.toString(id)); 4588304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final SharedPreferences.Editor ed = prefs.edit(); 4598304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook ed.putStringSet(PREF_SNOOZE_IDS, snoozedIds); 4608304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook ed.putLong(getAlarmPrefSnoozeTimeKey(id), time); 461c6098ef1fc69fe117343cc2e3db0d2b97b2daa6fBrad Fitzpatrick ed.apply(); 4625d6f5fa51fa7fd13784a73199161deabec5bc898Patrick Scott } 463d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // Set the next alert after updating the snooze. 464d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott setNextAlert(context); 4655fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 4665fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 4678304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook private static String getAlarmPrefSnoozeTimeKey(int id) { 4688304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook return getAlarmPrefSnoozeTimeKey(Integer.toString(id)); 4698304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } 4708304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 4718304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook private static String getAlarmPrefSnoozeTimeKey(String id) { 4728304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook return PREF_SNOOZE_TIME + id; 4738304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } 4748304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 4755fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 476d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott * Disable the snooze alert if the given id matches the snooze id. 4775fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 478d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott static void disableSnoozeAlert(final Context context, final int id) { 4795fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project SharedPreferences prefs = context.getSharedPreferences( 4805fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project AlarmClock.PREFERENCES, 0); 4818304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook if (hasAlarmBeenSnoozed(prefs, id)) { 482d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // This is the same id so clear the shared prefs. 4838304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook clearSnoozePreference(context, prefs, id); 484d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott } 4855fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 4865fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 487e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott // Helper to remove the snooze preference. Do not use clear because that 488c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott // will erase the clock preferences. Also clear the snooze notification in 489c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott // the window shade. 490c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott private static void clearSnoozePreference(final Context context, 4918304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final SharedPreferences prefs, final int id) { 4928304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final String alarmStr = Integer.toString(id); 4938304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final Set<String> snoozedIds = 4948304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook prefs.getStringSet(PREF_SNOOZE_IDS, new HashSet<String>()); 4958304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook if (snoozedIds.contains(alarmStr)) { 496c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott NotificationManager nm = (NotificationManager) 497c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott context.getSystemService(Context.NOTIFICATION_SERVICE); 4988304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook nm.cancel(id); 499c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott } 500c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott 501c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott final SharedPreferences.Editor ed = prefs.edit(); 5028304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook snoozedIds.remove(alarmStr); 5038304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook ed.putStringSet(PREF_SNOOZE_IDS, snoozedIds); 5048304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook ed.remove(getAlarmPrefSnoozeTimeKey(alarmStr)); 5058304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook ed.apply(); 5068304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } 5078304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 5088304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook private static void clearAllSnoozePreferences(final Context context, 5098304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final SharedPreferences prefs) { 5108304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook NotificationManager nm = (NotificationManager) 5118304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook context.getSystemService(Context.NOTIFICATION_SERVICE); 5128304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final Set<String> snoozedIds = 5138304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook prefs.getStringSet(PREF_SNOOZE_IDS, new HashSet<String>()); 5148304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final SharedPreferences.Editor ed = prefs.edit(); 5158304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook for (String snoozeId : snoozedIds) { 5168304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook nm.cancel(Integer.parseInt(snoozeId)); 5178304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook ed.remove(getAlarmPrefSnoozeTimeKey(snoozeId)); 5188304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } 5198304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 5208304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook ed.remove(PREF_SNOOZE_IDS); 521c6098ef1fc69fe117343cc2e3db0d2b97b2daa6fBrad Fitzpatrick ed.apply(); 5228304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } 5238304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 5248304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook private static boolean hasAlarmBeenSnoozed(final SharedPreferences prefs, final int alarmId) { 5258304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final Set<String> snoozedIds = prefs.getStringSet(PREF_SNOOZE_IDS, null); 5268304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook 5278304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook // Return true if there a valid snoozed alarmId was saved 5288304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook return snoozedIds != null && snoozedIds.contains(Integer.toString(alarmId)); 5298304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook } 530e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott 5315fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 5328304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook * Updates the specified Alarm with the additional snooze time. 5338304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook * Returns a boolean indicating whether the alarm was updated. 5345fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 5358304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook private static boolean updateAlarmTimeForSnooze( 5368304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final SharedPreferences prefs, final Alarm alarm) { 5378304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook if (!hasAlarmBeenSnoozed(prefs, alarm.id)) { 5388304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook // No need to modify the alarm 539d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott return false; 540d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott } 541d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott 5428304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook final long time = prefs.getLong(getAlarmPrefSnoozeTimeKey(alarm.id), -1); 543d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // The time in the database is either 0 (repeating) or a specific time 544d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // for a non-repeating alarm. Update this value so the AlarmReceiver 545d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott // has the right time to compare. 546d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott alarm.time = time; 547d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott 5485fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project return true; 5495fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 5505fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 5515fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 5525fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * Tells the StatusBar whether the alarm is enabled or disabled 5535fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 5545fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project private static void setStatusBarIcon(Context context, boolean enabled) { 555fe8dd39ee5e173f15337d09c36aa453a55364e98Patrick Scott Intent alarmChanged = new Intent("android.intent.action.ALARM_CHANGED"); 5565fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project alarmChanged.putExtra("alarmSet", enabled); 5575fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project context.sendBroadcast(alarmChanged); 5585fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 5595fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 560e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott private static long calculateAlarm(Alarm alarm) { 561e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott return calculateAlarm(alarm.hour, alarm.minutes, alarm.daysOfWeek) 562e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott .getTimeInMillis(); 563e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott } 564e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott 5655fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 5665fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * Given an alarm in hours and minutes, return a time suitable for 5675fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * setting in AlarmManager. 5685fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 569e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott static Calendar calculateAlarm(int hour, int minute, 570e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott Alarm.DaysOfWeek daysOfWeek) { 5715fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 5725fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project // start with now 5735fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project Calendar c = Calendar.getInstance(); 5745fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project c.setTimeInMillis(System.currentTimeMillis()); 5755fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 5765fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project int nowHour = c.get(Calendar.HOUR_OF_DAY); 5775fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project int nowMinute = c.get(Calendar.MINUTE); 5785fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 5795fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project // if alarm is behind current time, advance one day 5805fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project if (hour < nowHour || 5815fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project hour == nowHour && minute <= nowMinute) { 5825fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project c.add(Calendar.DAY_OF_YEAR, 1); 5835fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 5845fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project c.set(Calendar.HOUR_OF_DAY, hour); 5855fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project c.set(Calendar.MINUTE, minute); 5865fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project c.set(Calendar.SECOND, 0); 5875fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project c.set(Calendar.MILLISECOND, 0); 5885fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 5895fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project int addDays = daysOfWeek.getNextAlarm(c); 5905fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project if (addDays > 0) c.add(Calendar.DAY_OF_WEEK, addDays); 5915fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project return c; 5925fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 5935fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 5945fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project static String formatTime(final Context context, int hour, int minute, 595d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott Alarm.DaysOfWeek daysOfWeek) { 5965fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project Calendar c = calculateAlarm(hour, minute, daysOfWeek); 5975fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project return formatTime(context, c); 5985fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 5995fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 6005fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /* used by AlarmAlert */ 6015fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project static String formatTime(final Context context, Calendar c) { 6025fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project String format = get24HourMode(context) ? M24 : M12; 6035fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project return (c == null) ? "" : (String)DateFormat.format(format, c); 6045fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 6055fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 6065fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 6075fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * Shows day and time -- used for lock screen 6085fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 6095fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project private static String formatDayAndTime(final Context context, Calendar c) { 6105fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project String format = get24HourMode(context) ? DM24 : DM12; 6115fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project return (c == null) ? "" : (String)DateFormat.format(format, c); 6125fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 6135fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 6145fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 6155fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * Save time of the next alarm, as a formatted string, into the system 6165fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * settings so those who care can make use of it. 6175fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 6185fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project static void saveNextAlarm(final Context context, String timeString) { 6195fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project Settings.System.putString(context.getContentResolver(), 6205fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project Settings.System.NEXT_ALARM_FORMATTED, 6215fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project timeString); 6225fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 6235fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project 6245fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project /** 6255fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * @return true if clock is set to 24-hour mode 6265fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */ 6275fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project static boolean get24HourMode(final Context context) { 6285fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project return android.text.format.DateFormat.is24HourFormat(context); 6295fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project } 6305fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project} 631