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
47a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram    // A public action sent by AlarmKlaxon when the alarm has stopped sounding
48a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram    // for any reason (e.g. because it has been dismissed from AlarmAlertFullScreen,
49a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram    // or killed due to an incoming phone call, etc).
50a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram    public static final String ALARM_DONE_ACTION = "com.android.deskclock.ALARM_DONE";
51a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram
52a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram    // AlarmAlertFullScreen listens for this broadcast intent, so that other applications
53a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram    // can snooze the alarm (after ALARM_ALERT_ACTION and before ALARM_DONE_ACTION).
54a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram    public static final String ALARM_SNOOZE_ACTION = "com.android.deskclock.ALARM_SNOOZE";
55a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram
56a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram    // AlarmAlertFullScreen listens for this broadcast intent, so that other applications
57a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram    // can dismiss the alarm (after ALARM_ALERT_ACTION and before ALARM_DONE_ACTION).
58a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram    public static final String ALARM_DISMISS_ACTION = "com.android.deskclock.ALARM_DISMISS";
59a3aba0c93c31e89045cca669418e85acfb4e22fbDavid Ingram
60d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // This is a private action used by the AlarmKlaxon to update the UI to
61d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // show the alarm has been killed.
62d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String ALARM_KILLED = "alarm_killed";
635fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
64d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // Extra in the ALARM_KILLED intent to indicate to the user how long the
65d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // alarm played before being killed.
66d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String ALARM_KILLED_TIMEOUT = "alarm_killed_timeout";
675fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
68d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // This string is used to indicate a silent alarm in the db.
69d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String ALARM_ALERT_SILENT = "silent";
705fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
71d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // This intent is sent from the notification when the user cancels the
72d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // snooze alert.
73d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String CANCEL_SNOOZE = "cancel_snooze";
745fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
75d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // This string is used when passing an Alarm object through an intent.
76d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String ALARM_INTENT_EXTRA = "intent.extra.alarm";
775fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
78d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // This extra is the raw Alarm object data. It is used in the
79d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // AlarmManagerService to avoid a ClassNotFoundException when filling in
80d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // the Intent extras.
81d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String ALARM_RAW_DATA = "intent.extra.alarm_raw";
825fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
83d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // This string is used to identify the alarm id passed to SetAlarm from the
84d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // list of alarms.
85d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static final String ALARM_ID = "alarm_id";
865fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
87d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    final static String PREF_SNOOZE_ID = "snooze_id";
88d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    final static String PREF_SNOOZE_TIME = "snooze_time";
895fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
90d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private final static String DM12 = "E h:mm aa";
91d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private final static String DM24 = "E k:mm";
925fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
93d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private final static String M12 = "h:mm aa";
941ebd23eaa82ab560f5c7d8aeca8df7512df8424ePatrick Scott    // Shared with DigitalClock
951ebd23eaa82ab560f5c7d8aeca8df7512df8424ePatrick Scott    final static String M24 = "kk:mm";
965fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
975fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
986064d3f7aabcb073dc3f917e049bc04c67fb6b29Patrick Scott     * Creates a new Alarm and fills in the given alarm's id.
995fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
100e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    public static long addAlarm(Context context, Alarm alarm) {
101e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        ContentValues values = createContentValues(alarm);
1026064d3f7aabcb073dc3f917e049bc04c67fb6b29Patrick Scott        Uri uri = context.getContentResolver().insert(
1036064d3f7aabcb073dc3f917e049bc04c67fb6b29Patrick Scott                Alarm.Columns.CONTENT_URI, values);
1046064d3f7aabcb073dc3f917e049bc04c67fb6b29Patrick Scott        alarm.id = (int) ContentUris.parseId(uri);
105e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
106e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        long timeInMillis = calculateAlarm(alarm);
107e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        if (alarm.enabled) {
108e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott            clearSnoozeIfNeeded(context, timeInMillis);
109e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        }
110e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        setNextAlert(context);
111e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        return timeInMillis;
1125fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
1135fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
1145fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
1155fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Removes an existing Alarm.  If this alarm is snoozing, disables
1165fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * snooze.  Sets next alert.
1175fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
118d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static void deleteAlarm(
1195fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            Context context, int alarmId) {
1205fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
1215fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        ContentResolver contentResolver = context.getContentResolver();
1225fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        /* If alarm is snoozing, lose it */
123d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        disableSnoozeAlert(context, alarmId);
1245fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
125d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Uri uri = ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarmId);
126d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        contentResolver.delete(uri, "", null);
1275fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
1285fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        setNextAlert(context);
1295fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
1305fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
1315fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
1325fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Queries all alarms
1335fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @return cursor over all alarms
1345fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
135d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static Cursor getAlarmsCursor(ContentResolver contentResolver) {
1365fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return contentResolver.query(
137d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                Alarm.Columns.CONTENT_URI, Alarm.Columns.ALARM_QUERY_COLUMNS,
138d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                null, null, Alarm.Columns.DEFAULT_SORT_ORDER);
1395fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
1405fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
141d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // Private method to get a more limited set of alarms from the database.
142d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private static Cursor getFilteredAlarmsCursor(
143d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            ContentResolver contentResolver) {
144d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        return contentResolver.query(Alarm.Columns.CONTENT_URI,
145d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                Alarm.Columns.ALARM_QUERY_COLUMNS, Alarm.Columns.WHERE_ENABLED,
146d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                null, null);
1475fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
1485fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
149e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    private static ContentValues createContentValues(Alarm alarm) {
150e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        ContentValues values = new ContentValues(8);
151e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        // Set the alarm_time value if this alarm does not repeat. This will be
152e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        // used later to disable expire alarms.
153e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        long time = 0;
154e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        if (!alarm.daysOfWeek.isRepeatSet()) {
155e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott            time = calculateAlarm(alarm);
156e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        }
157e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
158e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.ENABLED, alarm.enabled ? 1 : 0);
159e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.HOUR, alarm.hour);
160e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.MINUTES, alarm.minutes);
161e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.ALARM_TIME, alarm.time);
162e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.DAYS_OF_WEEK, alarm.daysOfWeek.getCoded());
163e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.VIBRATE, alarm.vibrate);
164e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.MESSAGE, alarm.label);
165e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
166e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        // A null alert Uri indicates a silent alarm.
167e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        values.put(Alarm.Columns.ALERT, alarm.alert == null ? ALARM_ALERT_SILENT
168e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                : alarm.alert.toString());
169e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
170e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        return values;
171e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    }
172e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
173e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    private static void clearSnoozeIfNeeded(Context context, long alarmTime) {
174e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        // If this alarm fires before the next snooze, clear the snooze to
175e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        // enable this alarm.
176e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        SharedPreferences prefs =
177e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                context.getSharedPreferences(AlarmClock.PREFERENCES, 0);
178e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        long snoozeTime = prefs.getLong(PREF_SNOOZE_TIME, 0);
179e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        if (alarmTime < snoozeTime) {
180e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott            clearSnoozePreference(context, prefs);
181e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        }
182e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    }
183e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
1845fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
185d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott     * Return an Alarm object representing the alarm id in the database.
186d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott     * Returns null if no alarm exists.
1875fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
188d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static Alarm getAlarm(ContentResolver contentResolver, int alarmId) {
1895fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        Cursor cursor = contentResolver.query(
190d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarmId),
191d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                Alarm.Columns.ALARM_QUERY_COLUMNS,
192d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                null, null, null);
193d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Alarm alarm = null;
194d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (cursor != null) {
195d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            if (cursor.moveToFirst()) {
196d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                alarm = new Alarm(cursor);
197d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            }
198d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            cursor.close();
199d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        }
200d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        return alarm;
2015fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
2025fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
2035fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
2045fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
2055fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * A convenience method to set an alarm in the Alarms
2065fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * content provider.
207c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott     * @return Time when the alarm will fire.
2085fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
209e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    public static long setAlarm(Context context, Alarm alarm) {
210e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        ContentValues values = createContentValues(alarm);
2115fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        ContentResolver resolver = context.getContentResolver();
212e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        resolver.update(
213e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarm.id),
214e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                values, null, null);
2155fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
216e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        long timeInMillis = calculateAlarm(alarm);
217c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott
218e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        if (alarm.enabled) {
219232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // Disable the snooze if we just changed the snoozed alarm. This
220232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // only does work if the snoozed alarm is the same as the given
221232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // alarm.
222232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // TODO: disableSnoozeAlert should have a better name.
223232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            disableSnoozeAlert(context, alarm.id);
224232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott
225232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // Disable the snooze if this alarm fires before the snoozed alarm.
226232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // This works on every alarm since the user most likely intends to
227232725e8c1b1b85a44857cedcc842dcd2c956dc2Patrick Scott            // have the modified alarm fire next.
228e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott            clearSnoozeIfNeeded(context, timeInMillis);
229c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott        }
230c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott
2315fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        setNextAlert(context);
232c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott
233c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott        return timeInMillis;
2345fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
2355fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
2365fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
2375fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * A convenience method to enable or disable an alarm.
2385fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     *
2395fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @param id             corresponds to the _id column
2405fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @param enabled        corresponds to the ENABLED column
2415fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
2425fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
243d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static void enableAlarm(
2445fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            final Context context, final int id, boolean enabled) {
2455fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        enableAlarmInternal(context, id, enabled);
2465fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        setNextAlert(context);
2475fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
2485fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
249d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private static void enableAlarmInternal(final Context context,
250d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            final int id, boolean enabled) {
251d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        enableAlarmInternal(context, getAlarm(context.getContentResolver(), id),
252d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                enabled);
253d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    }
2545fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
255d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private static void enableAlarmInternal(final Context context,
256d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            final Alarm alarm, boolean enabled) {
25722924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott        if (alarm == null) {
25822924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott            return;
25922924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott        }
260d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        ContentResolver resolver = context.getContentResolver();
2615fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
2625fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        ContentValues values = new ContentValues(2);
263d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        values.put(Alarm.Columns.ENABLED, enabled ? 1 : 0);
2645fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
265d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // If we are enabling the alarm, calculate alarm time since the time
266d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // value in Alarm may be old.
2675fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        if (enabled) {
268d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            long time = 0;
269d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            if (!alarm.daysOfWeek.isRepeatSet()) {
270e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                time = calculateAlarm(alarm);
2715fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            }
272d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            values.put(Alarm.Columns.ALARM_TIME, time);
27322924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott        } else {
27422924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott            // Clear the snooze if the id matches.
27522924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott            disableSnoozeAlert(context, alarm.id);
2765fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        }
2775fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
278d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        resolver.update(ContentUris.withAppendedId(
279d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                Alarm.Columns.CONTENT_URI, alarm.id), values, null, null);
2805fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
2815fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
282d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public static Alarm calculateNextAlert(final Context context) {
283d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Alarm alarm = null;
284d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        long minTime = Long.MAX_VALUE;
28598948f9ce1dabcc79350a847baf220635b0c087aPatrick Scott        long now = System.currentTimeMillis();
286d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Cursor cursor = getFilteredAlarmsCursor(context.getContentResolver());
287d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (cursor != null) {
288d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            if (cursor.moveToFirst()) {
289d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                do {
290d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    Alarm a = new Alarm(cursor);
291d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    // A time of 0 indicates this is a repeating alarm, so
292d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    // calculate the time to get the next alert.
293d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    if (a.time == 0) {
294e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                        a.time = calculateAlarm(a);
29598948f9ce1dabcc79350a847baf220635b0c087aPatrick Scott                    } else if (a.time < now) {
29698948f9ce1dabcc79350a847baf220635b0c087aPatrick Scott                        // Expired alarm, disable it and move along.
29798948f9ce1dabcc79350a847baf220635b0c087aPatrick Scott                        enableAlarmInternal(context, a, false);
29898948f9ce1dabcc79350a847baf220635b0c087aPatrick Scott                        continue;
299d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    }
300d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    if (a.time < minTime) {
301d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                        minTime = a.time;
302d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                        alarm = a;
303d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    }
304d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                } while (cursor.moveToNext());
3055fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            }
306d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            cursor.close();
3075fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        }
308d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        return alarm;
3095fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
3105fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
3115fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
3125fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Disables non-repeating alarms that have passed.  Called at
3135fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * boot.
3145fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
3155fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    public static void disableExpiredAlarms(final Context context) {
316d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Cursor cur = getFilteredAlarmsCursor(context.getContentResolver());
3175fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        long now = System.currentTimeMillis();
3185fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
3195fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        if (cur.moveToFirst()) {
3205fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            do {
321d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                Alarm alarm = new Alarm(cur);
322d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                // A time of 0 means this alarm repeats. If the time is
323d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                // non-zero, check if the time is before now.
324d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                if (alarm.time != 0 && alarm.time < now) {
325d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    if (Log.LOGV) {
326d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                        Log.v("** DISABLE " + alarm.id + " now " + now +" set "
327d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                                + alarm.time);
328d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    }
329d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                    enableAlarmInternal(context, alarm, false);
3305fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                }
3315fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            } while (cur.moveToNext());
3325fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        }
3335fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        cur.close();
3345fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
3355fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
3365fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
3375fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Called at system startup, on time/timezone change, and whenever
3385fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * the user changes alarm settings.  Activates snooze if set,
3395fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * otherwise loads all alarms, activates next alert.
3405fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
3415fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    public static void setNextAlert(final Context context) {
342d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (!enableSnoozeAlert(context)) {
343d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            Alarm alarm = calculateNextAlert(context);
344d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            if (alarm != null) {
345d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                enableAlert(context, alarm, alarm.time);
3465fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            } else {
347d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                disableAlert(context);
3485fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            }
3495fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        }
3505fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
3515fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
3525fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
3535fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Sets alert in AlarmManger and StatusBar.  This is what will
3545fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * actually launch the alert when the alarm triggers.
3555fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     *
356d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott     * @param alarm Alarm.
3575fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @param atTimeInMillis milliseconds since epoch
3585fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
359d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    private static void enableAlert(Context context, final Alarm alarm,
360d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            final long atTimeInMillis) {
3615fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        AlarmManager am = (AlarmManager)
3625fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                context.getSystemService(Context.ALARM_SERVICE);
3635fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
364d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (Log.LOGV) {
365d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            Log.v("** setAlert id " + alarm.id + " atTime " + atTimeInMillis);
3665d6f5fa51fa7fd13784a73199161deabec5bc898Patrick Scott        }
367d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
368d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Intent intent = new Intent(ALARM_ALERT_ACTION);
369d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
370d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // XXX: This is a slight hack to avoid an exception in the remote
371d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // AlarmManagerService process. The AlarmManager adds extra data to
372d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // this Intent which causes it to inflate. Since the remote process
373d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // does not know about the Alarm class, it throws a
374d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // ClassNotFoundException.
375d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        //
376d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // To avoid this, we marshall the data ourselves and then parcel a plain
377d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // byte[] array. The AlarmReceiver class knows to build the Alarm
378d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // object from the byte[] array.
379d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        Parcel out = Parcel.obtain();
380d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        alarm.writeToParcel(out, 0);
381d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        out.setDataPosition(0);
382d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        intent.putExtra(ALARM_RAW_DATA, out.marshall());
383d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
3845fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        PendingIntent sender = PendingIntent.getBroadcast(
3855fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
3865fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
387d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender);
3885fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
3895fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        setStatusBarIcon(context, true);
3905fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
3915fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        Calendar c = Calendar.getInstance();
392e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        c.setTimeInMillis(atTimeInMillis);
3935fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        String timeString = formatDayAndTime(context, c);
3945fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        saveNextAlarm(context, timeString);
3955fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
3965fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
3975fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
3985fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Disables alert in AlarmManger and StatusBar.
3995fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     *
4005fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @param id Alarm ID.
4015fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
402d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    static void disableAlert(Context context) {
4035fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        AlarmManager am = (AlarmManager)
4045fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                context.getSystemService(Context.ALARM_SERVICE);
4055fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        PendingIntent sender = PendingIntent.getBroadcast(
406d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                context, 0, new Intent(ALARM_ALERT_ACTION),
407d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                PendingIntent.FLAG_CANCEL_CURRENT);
4085fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        am.cancel(sender);
4095fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        setStatusBarIcon(context, false);
4105fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        saveNextAlarm(context, "");
4115fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
4125fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
413d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    static void saveSnoozeAlert(final Context context, final int id,
414d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            final long time) {
4155fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        SharedPreferences prefs = context.getSharedPreferences(
4165fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                AlarmClock.PREFERENCES, 0);
417d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (id == -1) {
418c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott            clearSnoozePreference(context, prefs);
419d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        } else {
420c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott            SharedPreferences.Editor ed = prefs.edit();
421d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            ed.putInt(PREF_SNOOZE_ID, id);
422d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            ed.putLong(PREF_SNOOZE_TIME, time);
423e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott            ed.commit();
4245d6f5fa51fa7fd13784a73199161deabec5bc898Patrick Scott        }
425d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // Set the next alert after updating the snooze.
426d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        setNextAlert(context);
4275fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
4285fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
4295fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
430d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott     * Disable the snooze alert if the given id matches the snooze id.
4315fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
432d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    static void disableSnoozeAlert(final Context context, final int id) {
4335fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        SharedPreferences prefs = context.getSharedPreferences(
4345fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                AlarmClock.PREFERENCES, 0);
435d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        int snoozeId = prefs.getInt(PREF_SNOOZE_ID, -1);
436d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (snoozeId == -1) {
437d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            // No snooze set, do nothing.
438d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            return;
439d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        } else if (snoozeId == id) {
440d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            // This is the same id so clear the shared prefs.
441c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott            clearSnoozePreference(context, prefs);
442d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        }
4435fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
4445fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
445e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott    // Helper to remove the snooze preference. Do not use clear because that
446c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott    // will erase the clock preferences. Also clear the snooze notification in
447c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott    // the window shade.
448c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott    private static void clearSnoozePreference(final Context context,
449c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott            final SharedPreferences prefs) {
450c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott        final int alarmId = prefs.getInt(PREF_SNOOZE_ID, -1);
451c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott        if (alarmId != -1) {
452c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott            NotificationManager nm = (NotificationManager)
453c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott                    context.getSystemService(Context.NOTIFICATION_SERVICE);
454c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott            nm.cancel(alarmId);
455c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott        }
456c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott
457c7edd6ea840e25cb04d090b6c29dc96c04d2be40Patrick Scott        final SharedPreferences.Editor ed = prefs.edit();
458e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott        ed.remove(PREF_SNOOZE_ID);
459e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott        ed.remove(PREF_SNOOZE_TIME);
460e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott        ed.commit();
461e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott    };
462e77fa5af5ac139f69ad504dbadd6f6aaab8c064dPatrick Scott
4635fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
4645fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * If there is a snooze set, enable it in AlarmManager
4655fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @return true if snooze is set
4665fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
4675fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    private static boolean enableSnoozeAlert(final Context context) {
4685fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        SharedPreferences prefs = context.getSharedPreferences(
4695fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                AlarmClock.PREFERENCES, 0);
4705fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
4715fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        int id = prefs.getInt(PREF_SNOOZE_ID, -1);
472d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        if (id == -1) {
473d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott            return false;
474d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        }
475d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        long time = prefs.getLong(PREF_SNOOZE_TIME, -1);
476d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
477d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // Get the alarm from the db.
478d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        final Alarm alarm = getAlarm(context.getContentResolver(), id);
47922924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott        if (alarm == null) {
48022924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott            return false;
48122924b6afee333e16fb62a19f1ac044c87c528b5Patrick Scott        }
482d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // The time in the database is either 0 (repeating) or a specific time
483d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // for a non-repeating alarm. Update this value so the AlarmReceiver
484d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        // has the right time to compare.
485d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        alarm.time = time;
486d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
487d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        enableAlert(context, alarm, time);
4885fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return true;
4895fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
4905fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
4915fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
4925fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Tells the StatusBar whether the alarm is enabled or disabled
4935fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
4945fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    private static void setStatusBarIcon(Context context, boolean enabled) {
495fe8dd39ee5e173f15337d09c36aa453a55364e98Patrick Scott        Intent alarmChanged = new Intent("android.intent.action.ALARM_CHANGED");
4965fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        alarmChanged.putExtra("alarmSet", enabled);
4975fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        context.sendBroadcast(alarmChanged);
4985fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
4995fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
500e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    private static long calculateAlarm(Alarm alarm) {
501e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott        return calculateAlarm(alarm.hour, alarm.minutes, alarm.daysOfWeek)
502e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott                .getTimeInMillis();
503e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    }
504e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott
5055fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
5065fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Given an alarm in hours and minutes, return a time suitable for
5075fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * setting in AlarmManager.
5085fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
509e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott    static Calendar calculateAlarm(int hour, int minute,
510e46be904bd76dff5a13aeef12e9bfeddac7bbe81Patrick Scott            Alarm.DaysOfWeek daysOfWeek) {
5115fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5125fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        // start with now
5135fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        Calendar c = Calendar.getInstance();
5145fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        c.setTimeInMillis(System.currentTimeMillis());
5155fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5165fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        int nowHour = c.get(Calendar.HOUR_OF_DAY);
5175fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        int nowMinute = c.get(Calendar.MINUTE);
5185fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5195fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        // if alarm is behind current time, advance one day
5205fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        if (hour < nowHour  ||
5215fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            hour == nowHour && minute <= nowMinute) {
5225fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project            c.add(Calendar.DAY_OF_YEAR, 1);
5235fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        }
5245fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        c.set(Calendar.HOUR_OF_DAY, hour);
5255fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        c.set(Calendar.MINUTE, minute);
5265fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        c.set(Calendar.SECOND, 0);
5275fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        c.set(Calendar.MILLISECOND, 0);
5285fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5295fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        int addDays = daysOfWeek.getNextAlarm(c);
5305fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        if (addDays > 0) c.add(Calendar.DAY_OF_WEEK, addDays);
5315fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return c;
5325fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
5335fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5345fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    static String formatTime(final Context context, int hour, int minute,
535d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott                             Alarm.DaysOfWeek daysOfWeek) {
5365fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        Calendar c = calculateAlarm(hour, minute, daysOfWeek);
5375fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return formatTime(context, c);
5385fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
5395fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5405fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /* used by AlarmAlert */
5415fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    static String formatTime(final Context context, Calendar c) {
5425fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        String format = get24HourMode(context) ? M24 : M12;
5435fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return (c == null) ? "" : (String)DateFormat.format(format, c);
5445fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
5455fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5465fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
5475fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Shows day and time -- used for lock screen
5485fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
5495fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    private static String formatDayAndTime(final Context context, Calendar c) {
5505fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        String format = get24HourMode(context) ? DM24 : DM12;
5515fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return (c == null) ? "" : (String)DateFormat.format(format, c);
5525fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
5535fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5545fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
5555fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * Save time of the next alarm, as a formatted string, into the system
5565fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * settings so those who care can make use of it.
5575fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
5585fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    static void saveNextAlarm(final Context context, String timeString) {
5595fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        Settings.System.putString(context.getContentResolver(),
5605fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                                  Settings.System.NEXT_ALARM_FORMATTED,
5615fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project                                  timeString);
5625fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
5635fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project
5645fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    /**
5655fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     * @return true if clock is set to 24-hour mode
5665fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project     */
5675fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    static boolean get24HourMode(final Context context) {
5685fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project        return android.text.format.DateFormat.is24HourFormat(context);
5695fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project    }
5705fedae0fb8cc29db88719329d52bdd62aad14277The Android Open Source Project}
571