Alarms.java revision 232725e8c1b1b85a44857cedcc842dcd2c956dc2
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;
35eb142ac59aa8039c409b5fe4f50a130f16960d41Eric Fischerimport java.text.DateFormatSymbols;
365fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
375fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project/**
385fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project * The Alarms provider supplies info about Alarm Clock settings
395fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project */
405fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Projectpublic class Alarms {
415fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
42d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // This action triggers the AlarmReceiver as well as the AlarmKlaxon. It
43d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // is a public action used in the manifest for receiving Alarm broadcasts
44d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // from the alarm manager.
45fa954248b41b567dfa2ddd7bd063d3936a51758eDaniel Sandler    public static final String ALARM_ALERT_ACTION = "com.android.deskclock.ALARM_ALERT";
465fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
47d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // This is a private action used by the AlarmKlaxon to update the UI to
48d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // show the alarm has been killed.
49d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String ALARM_KILLED = "alarm_killed";
505fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
51d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // Extra in the ALARM_KILLED intent to indicate to the user how long the
52d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // alarm played before being killed.
53d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String ALARM_KILLED_TIMEOUT = "alarm_killed_timeout";
545fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
55d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // This string is used to indicate a silent alarm in the db.
56d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String ALARM_ALERT_SILENT = "silent";
575fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
58d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // This intent is sent from the notification when the user cancels the
59d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // snooze alert.
60d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String CANCEL_SNOOZE = "cancel_snooze";
615fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
62d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // This string is used when passing an Alarm object through an intent.
63d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String ALARM_INTENT_EXTRA = "intent.extra.alarm";
645fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
65d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // This extra is the raw Alarm object data. It is used in the
66d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // AlarmManagerService to avoid a ClassNotFoundException when filling in
67d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // the Intent extras.
68d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String ALARM_RAW_DATA = "intent.extra.alarm_raw";
695fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
70d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // This string is used to identify the alarm id passed to SetAlarm from the
71d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // list of alarms.
72d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String ALARM_ID = "alarm_id";
735fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
74d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    final static String PREF_SNOOZE_ID = "snooze_id";
75d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    final static String PREF_SNOOZE_TIME = "snooze_time";
765fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
77d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private final static String DM12 = "E h:mm aa";
78d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private final static String DM24 = "E k:mm";
795fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
80d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private final static String M12 = "h:mm aa";
811ebd23eaa82ab560f5c7d8aeca8df7512df8424ePatrick Scott    // Shared with DigitalClock
821ebd23eaa82ab560f5c7d8aeca8df7512df8424ePatrick Scott    final static String M24 = "kk:mm";
835fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
845fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
855fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Creates a new Alarm.
865fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
87e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    public static long addAlarm(Context context, Alarm alarm) {
88e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        ContentValues values = createContentValues(alarm);
89e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        context.getContentResolver().insert(Alarm.Columns.CONTENT_URI, values);
90e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
91e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        long timeInMillis = calculateAlarm(alarm);
92e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        if (alarm.enabled) {
93e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott            clearSnoozeIfNeeded(context, timeInMillis);
94e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        }
95e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        setNextAlert(context);
96e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        return timeInMillis;
975fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
985fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
995fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
1005fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Removes an existing Alarm.  If this alarm is snoozing, disables
1015fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * snooze.  Sets next alert.
1025fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
103d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static void deleteAlarm(
1045fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            Context context, int alarmId) {
1055fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
1065fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        ContentResolver contentResolver = context.getContentResolver();
1075fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        /* If alarm is snoozing, lose it */
108d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        disableSnoozeAlert(context, alarmId);
1095fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
110d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Uri uri = ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarmId);
111d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        contentResolver.delete(uri, "", null);
1125fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
1135fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        setNextAlert(context);
1145fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
1155fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
1165fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
1175fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Queries all alarms
1185fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @return cursor over all alarms
1195fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
120d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static Cursor getAlarmsCursor(ContentResolver contentResolver) {
1215fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return contentResolver.query(
122d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                Alarm.Columns.CONTENT_URI, Alarm.Columns.ALARM_QUERY_COLUMNS,
123d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                null, null, Alarm.Columns.DEFAULT_SORT_ORDER);
1245fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
1255fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
126d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // Private method to get a more limited set of alarms from the database.
127d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private static Cursor getFilteredAlarmsCursor(
128d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            ContentResolver contentResolver) {
129d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        return contentResolver.query(Alarm.Columns.CONTENT_URI,
130d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                Alarm.Columns.ALARM_QUERY_COLUMNS, Alarm.Columns.WHERE_ENABLED,
131d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                null, null);
1325fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
1335fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
134e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    private static ContentValues createContentValues(Alarm alarm) {
135e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        ContentValues values = new ContentValues(8);
136e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        // Set the alarm_time value if this alarm does not repeat. This will be
137e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        // used later to disable expire alarms.
138e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        long time = 0;
139e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        if (!alarm.daysOfWeek.isRepeatSet()) {
140e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott            time = calculateAlarm(alarm);
141e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        }
142e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
143e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.ENABLED, alarm.enabled ? 1 : 0);
144e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.HOUR, alarm.hour);
145e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.MINUTES, alarm.minutes);
146e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.ALARM_TIME, alarm.time);
147e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.DAYS_OF_WEEK, alarm.daysOfWeek.getCoded());
148e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.VIBRATE, alarm.vibrate);
149e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.MESSAGE, alarm.label);
150e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
151e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        // A null alert Uri indicates a silent alarm.
152e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.ALERT, alarm.alert == null ? ALARM_ALERT_SILENT
153e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                : alarm.alert.toString());
154e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
155e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        return values;
156e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    }
157e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
158e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    private static void clearSnoozeIfNeeded(Context context, long alarmTime) {
159e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        // If this alarm fires before the next snooze, clear the snooze to
160e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        // enable this alarm.
161e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        SharedPreferences prefs =
162e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                context.getSharedPreferences(AlarmClock.PREFERENCES, 0);
163e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        long snoozeTime = prefs.getLong(PREF_SNOOZE_TIME, 0);
164e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        if (alarmTime < snoozeTime) {
165e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott            clearSnoozePreference(context, prefs);
166e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        }
167e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    }
168e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
1695fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
170d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott     * Return an Alarm object representing the alarm id in the database.
171d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott     * Returns null if no alarm exists.
1725fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
173d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static Alarm getAlarm(ContentResolver contentResolver, int alarmId) {
1745fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        Cursor cursor = contentResolver.query(
175d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarmId),
176d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                Alarm.Columns.ALARM_QUERY_COLUMNS,
177d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                null, null, null);
178d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Alarm alarm = null;
179d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (cursor != null) {
180d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            if (cursor.moveToFirst()) {
181d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                alarm = new Alarm(cursor);
182d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            }
183d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            cursor.close();
184d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        }
185d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        return alarm;
1865fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
1875fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
1885fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
1895fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
1905fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * A convenience method to set an alarm in the Alarms
1915fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * content provider.
192c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott     * @return Time when the alarm will fire.
1935fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
194e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    public static long setAlarm(Context context, Alarm alarm) {
195e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        ContentValues values = createContentValues(alarm);
1965fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        ContentResolver resolver = context.getContentResolver();
197e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        resolver.update(
198e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarm.id),
199e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                values, null, null);
2005fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
201e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        long timeInMillis = calculateAlarm(alarm);
202c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott
203e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        if (alarm.enabled) {
204232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // Disable the snooze if we just changed the snoozed alarm. This
205232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // only does work if the snoozed alarm is the same as the given
206232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // alarm.
207232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // TODO: disableSnoozeAlert should have a better name.
208232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            disableSnoozeAlert(context, alarm.id);
209232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott
210232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // Disable the snooze if this alarm fires before the snoozed alarm.
211232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // This works on every alarm since the user most likely intends to
212232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // have the modified alarm fire next.
213e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott            clearSnoozeIfNeeded(context, timeInMillis);
214c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott        }
215c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott
2165fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        setNextAlert(context);
217c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott
218c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott        return timeInMillis;
2195fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
2205fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
2215fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
2225fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * A convenience method to enable or disable an alarm.
2235fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     *
2245fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @param id             corresponds to the _id column
2255fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @param enabled        corresponds to the ENABLED column
2265fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
2275fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
228d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static void enableAlarm(
2295fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            final Context context, final int id, boolean enabled) {
2305fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        enableAlarmInternal(context, id, enabled);
2315fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        setNextAlert(context);
2325fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
2335fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
234d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private static void enableAlarmInternal(final Context context,
235d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            final int id, boolean enabled) {
236d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        enableAlarmInternal(context, getAlarm(context.getContentResolver(), id),
237d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                enabled);
238d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    }
2395fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
240d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private static void enableAlarmInternal(final Context context,
241d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            final Alarm alarm, boolean enabled) {
24222924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott        if (alarm == null) {
24322924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott            return;
24422924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott        }
245d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        ContentResolver resolver = context.getContentResolver();
2465fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
2475fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        ContentValues values = new ContentValues(2);
248d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        values.put(Alarm.Columns.ENABLED, enabled ? 1 : 0);
2495fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
250d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // If we are enabling the alarm, calculate alarm time since the time
251d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // value in Alarm may be old.
2525fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        if (enabled) {
253d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            long time = 0;
254d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            if (!alarm.daysOfWeek.isRepeatSet()) {
255e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                time = calculateAlarm(alarm);
2565fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            }
257d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            values.put(Alarm.Columns.ALARM_TIME, time);
25822924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott        } else {
25922924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott            // Clear the snooze if the id matches.
26022924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott            disableSnoozeAlert(context, alarm.id);
2615fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        }
2625fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
263d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        resolver.update(ContentUris.withAppendedId(
264d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                Alarm.Columns.CONTENT_URI, alarm.id), values, null, null);
2655fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
2665fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
267d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static Alarm calculateNextAlert(final Context context) {
268d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Alarm alarm = null;
269d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        long minTime = Long.MAX_VALUE;
27098948f9ce1dabcc79350a847baf220635b0c087aPatrick Scott        long now = System.currentTimeMillis();
271d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Cursor cursor = getFilteredAlarmsCursor(context.getContentResolver());
272d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (cursor != null) {
273d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            if (cursor.moveToFirst()) {
274d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                do {
275d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    Alarm a = new Alarm(cursor);
276d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    // A time of 0 indicates this is a repeating alarm, so
277d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    // calculate the time to get the next alert.
278d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    if (a.time == 0) {
279e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                        a.time = calculateAlarm(a);
28098948f9ce1dabcc79350a847baf220635b0c087aPatrick Scott                    } else if (a.time < now) {
28198948f9ce1dabcc79350a847baf220635b0c087aPatrick Scott                        // Expired alarm, disable it and move along.
28298948f9ce1dabcc79350a847baf220635b0c087aPatrick Scott                        enableAlarmInternal(context, a, false);
28398948f9ce1dabcc79350a847baf220635b0c087aPatrick Scott                        continue;
284d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    }
285d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    if (a.time < minTime) {
286d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                        minTime = a.time;
287d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                        alarm = a;
288d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    }
289d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                } while (cursor.moveToNext());
2905fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            }
291d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            cursor.close();
2925fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        }
293d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        return alarm;
2945fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
2955fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
2965fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
2975fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Disables non-repeating alarms that have passed.  Called at
2985fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * boot.
2995fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
3005fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    public static void disableExpiredAlarms(final Context context) {
301d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Cursor cur = getFilteredAlarmsCursor(context.getContentResolver());
3025fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        long now = System.currentTimeMillis();
3035fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
3045fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        if (cur.moveToFirst()) {
3055fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            do {
306d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                Alarm alarm = new Alarm(cur);
307d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                // A time of 0 means this alarm repeats. If the time is
308d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                // non-zero, check if the time is before now.
309d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                if (alarm.time != 0 && alarm.time < now) {
310d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    if (Log.LOGV) {
311d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                        Log.v("** DISABLE " + alarm.id + " now " + now +" set "
312d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                                + alarm.time);
313d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    }
314d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    enableAlarmInternal(context, alarm, false);
3155fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                }
3165fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            } while (cur.moveToNext());
3175fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        }
3185fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        cur.close();
3195fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
3205fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
3215fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
3225fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Called at system startup, on time/timezone change, and whenever
3235fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * the user changes alarm settings.  Activates snooze if set,
3245fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * otherwise loads all alarms, activates next alert.
3255fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
3265fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    public static void setNextAlert(final Context context) {
327d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (!enableSnoozeAlert(context)) {
328d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            Alarm alarm = calculateNextAlert(context);
329d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            if (alarm != null) {
330d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                enableAlert(context, alarm, alarm.time);
3315fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            } else {
332d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                disableAlert(context);
3335fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            }
3345fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        }
3355fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
3365fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
3375fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
3385fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Sets alert in AlarmManger and StatusBar.  This is what will
3395fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * actually launch the alert when the alarm triggers.
3405fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     *
341d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott     * @param alarm Alarm.
3425fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @param atTimeInMillis milliseconds since epoch
3435fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
344d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private static void enableAlert(Context context, final Alarm alarm,
345d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            final long atTimeInMillis) {
3465fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        AlarmManager am = (AlarmManager)
3475fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                context.getSystemService(Context.ALARM_SERVICE);
3485fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
349d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (Log.LOGV) {
350d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            Log.v("** setAlert id " + alarm.id + " atTime " + atTimeInMillis);
3515d6f5fa51fa7fd13784a73199161deabec5bc898Patrick Scott        }
352d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
353d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Intent intent = new Intent(ALARM_ALERT_ACTION);
354d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
355d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // XXX: This is a slight hack to avoid an exception in the remote
356d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // AlarmManagerService process. The AlarmManager adds extra data to
357d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // this Intent which causes it to inflate. Since the remote process
358d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // does not know about the Alarm class, it throws a
359d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // ClassNotFoundException.
360d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        //
361d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // To avoid this, we marshall the data ourselves and then parcel a plain
362d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // byte[] array. The AlarmReceiver class knows to build the Alarm
363d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // object from the byte[] array.
364d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Parcel out = Parcel.obtain();
365d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        alarm.writeToParcel(out, 0);
366d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        out.setDataPosition(0);
367d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        intent.putExtra(ALARM_RAW_DATA, out.marshall());
368d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
3695fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        PendingIntent sender = PendingIntent.getBroadcast(
3705fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
3715fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
372d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender);
3735fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
3745fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        setStatusBarIcon(context, true);
3755fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
3765fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        Calendar c = Calendar.getInstance();
377e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        c.setTimeInMillis(atTimeInMillis);
3785fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        String timeString = formatDayAndTime(context, c);
3795fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        saveNextAlarm(context, timeString);
3805fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
3815fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
3825fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
3835fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Disables alert in AlarmManger and StatusBar.
3845fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     *
3855fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @param id Alarm ID.
3865fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
387d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    static void disableAlert(Context context) {
3885fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        AlarmManager am = (AlarmManager)
3895fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                context.getSystemService(Context.ALARM_SERVICE);
3905fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        PendingIntent sender = PendingIntent.getBroadcast(
391d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                context, 0, new Intent(ALARM_ALERT_ACTION),
392d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                PendingIntent.FLAG_CANCEL_CURRENT);
3935fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        am.cancel(sender);
3945fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        setStatusBarIcon(context, false);
3955fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        saveNextAlarm(context, "");
3965fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
3975fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
398d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    static void saveSnoozeAlert(final Context context, final int id,
399d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            final long time) {
4005fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        SharedPreferences prefs = context.getSharedPreferences(
4015fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                AlarmClock.PREFERENCES, 0);
402d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (id == -1) {
403c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott            clearSnoozePreference(context, prefs);
404d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        } else {
405c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott            SharedPreferences.Editor ed = prefs.edit();
406d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            ed.putInt(PREF_SNOOZE_ID, id);
407d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            ed.putLong(PREF_SNOOZE_TIME, time);
408e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott            ed.commit();
4095d6f5fa51fa7fd13784a73199161deabec5bc898Patrick Scott        }
410d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // Set the next alert after updating the snooze.
411d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        setNextAlert(context);
4125fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
4135fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
4145fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
415d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott     * Disable the snooze alert if the given id matches the snooze id.
4165fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
417d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    static void disableSnoozeAlert(final Context context, final int id) {
4185fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        SharedPreferences prefs = context.getSharedPreferences(
4195fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                AlarmClock.PREFERENCES, 0);
420d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        int snoozeId = prefs.getInt(PREF_SNOOZE_ID, -1);
421d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (snoozeId == -1) {
422d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            // No snooze set, do nothing.
423d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            return;
424d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        } else if (snoozeId == id) {
425d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            // This is the same id so clear the shared prefs.
426c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott            clearSnoozePreference(context, prefs);
427d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        }
4285fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
4295fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
430e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott    // Helper to remove the snooze preference. Do not use clear because that
431c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott    // will erase the clock preferences. Also clear the snooze notification in
432c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott    // the window shade.
433c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott    private static void clearSnoozePreference(final Context context,
434c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott            final SharedPreferences prefs) {
435c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott        final int alarmId = prefs.getInt(PREF_SNOOZE_ID, -1);
436c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott        if (alarmId != -1) {
437c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott            NotificationManager nm = (NotificationManager)
438c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott                    context.getSystemService(Context.NOTIFICATION_SERVICE);
439c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott            nm.cancel(alarmId);
440c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott        }
441c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott
442c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott        final SharedPreferences.Editor ed = prefs.edit();
443e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott        ed.remove(PREF_SNOOZE_ID);
444e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott        ed.remove(PREF_SNOOZE_TIME);
445e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott        ed.commit();
446e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott    };
447e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott
4485fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
4495fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * If there is a snooze set, enable it in AlarmManager
4505fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @return true if snooze is set
4515fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
4525fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    private static boolean enableSnoozeAlert(final Context context) {
4535fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        SharedPreferences prefs = context.getSharedPreferences(
4545fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                AlarmClock.PREFERENCES, 0);
4555fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
4565fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        int id = prefs.getInt(PREF_SNOOZE_ID, -1);
457d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (id == -1) {
458d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            return false;
459d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        }
460d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        long time = prefs.getLong(PREF_SNOOZE_TIME, -1);
461d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
462d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // Get the alarm from the db.
463d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        final Alarm alarm = getAlarm(context.getContentResolver(), id);
46422924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott        if (alarm == null) {
46522924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott            return false;
46622924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott        }
467d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // The time in the database is either 0 (repeating) or a specific time
468d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // for a non-repeating alarm. Update this value so the AlarmReceiver
469d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // has the right time to compare.
470d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        alarm.time = time;
471d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
472d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        enableAlert(context, alarm, time);
4735fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return true;
4745fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
4755fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
4765fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
4775fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Tells the StatusBar whether the alarm is enabled or disabled
4785fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
4795fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    private static void setStatusBarIcon(Context context, boolean enabled) {
480fe8dd39ee5e173f15337d09c36aa453a55364e98Patrick Scott        Intent alarmChanged = new Intent("android.intent.action.ALARM_CHANGED");
4815fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        alarmChanged.putExtra("alarmSet", enabled);
4825fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        context.sendBroadcast(alarmChanged);
4835fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
4845fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
485e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    private static long calculateAlarm(Alarm alarm) {
486e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        return calculateAlarm(alarm.hour, alarm.minutes, alarm.daysOfWeek)
487e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                .getTimeInMillis();
488e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    }
489e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
4905fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
4915fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Given an alarm in hours and minutes, return a time suitable for
4925fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * setting in AlarmManager.
4935fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
494e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    static Calendar calculateAlarm(int hour, int minute,
495e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott            Alarm.DaysOfWeek daysOfWeek) {
4965fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
4975fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        // start with now
4985fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        Calendar c = Calendar.getInstance();
4995fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        c.setTimeInMillis(System.currentTimeMillis());
5005fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5015fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        int nowHour = c.get(Calendar.HOUR_OF_DAY);
5025fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        int nowMinute = c.get(Calendar.MINUTE);
5035fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5045fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        // if alarm is behind current time, advance one day
5055fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        if (hour < nowHour  ||
5065fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            hour == nowHour && minute <= nowMinute) {
5075fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            c.add(Calendar.DAY_OF_YEAR, 1);
5085fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        }
5095fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        c.set(Calendar.HOUR_OF_DAY, hour);
5105fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        c.set(Calendar.MINUTE, minute);
5115fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        c.set(Calendar.SECOND, 0);
5125fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        c.set(Calendar.MILLISECOND, 0);
5135fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5145fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        int addDays = daysOfWeek.getNextAlarm(c);
5155fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        if (addDays > 0) c.add(Calendar.DAY_OF_WEEK, addDays);
5165fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return c;
5175fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
5185fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5195fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    static String formatTime(final Context context, int hour, int minute,
520d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                             Alarm.DaysOfWeek daysOfWeek) {
5215fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        Calendar c = calculateAlarm(hour, minute, daysOfWeek);
5225fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return formatTime(context, c);
5235fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
5245fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5255fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /* used by AlarmAlert */
5265fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    static String formatTime(final Context context, Calendar c) {
5275fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        String format = get24HourMode(context) ? M24 : M12;
5285fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return (c == null) ? "" : (String)DateFormat.format(format, c);
5295fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
5305fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5315fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
5325fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Shows day and time -- used for lock screen
5335fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
5345fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    private static String formatDayAndTime(final Context context, Calendar c) {
5355fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        String format = get24HourMode(context) ? DM24 : DM12;
5365fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return (c == null) ? "" : (String)DateFormat.format(format, c);
5375fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
5385fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5395fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
5405fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Save time of the next alarm, as a formatted string, into the system
5415fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * settings so those who care can make use of it.
5425fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
5435fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    static void saveNextAlarm(final Context context, String timeString) {
5445fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        Settings.System.putString(context.getContentResolver(),
5455fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                                  Settings.System.NEXT_ALARM_FORMATTED,
5465fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                                  timeString);
5475fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
5485fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5495fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
5505fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @return true if clock is set to 24-hour mode
5515fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
5525fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    static boolean get24HourMode(final Context context) {
5535fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return android.text.format.DateFormat.is24HourFormat(context);
5545fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
5555fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project}
556