1d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott/*
28188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski * Copyright (C) 2013 The Android Open Source Project
3d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott *
4d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott * Licensed under the Apache License, Version 2.0 (the "License");
5d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott * you may not use this file except in compliance with the License.
6d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott * You may obtain a copy of the License at
7d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott *
8d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott *      http://www.apache.org/licenses/LICENSE-2.0
9d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott *
10d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott * Unless required by applicable law or agreed to in writing, software
11d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott * distributed under the License is distributed on an "AS IS" BASIS,
12d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott * See the License for the specific language governing permissions and
14d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott * limitations under the License.
15d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott */
16d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
17b839a7d1f42a1f3d58887f5a3c7978791d508cdbPaul Sliwowskipackage com.android.deskclock.provider;
18d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
198188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowskiimport android.content.ContentResolver;
208188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowskiimport android.content.ContentUris;
218188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowskiimport android.content.ContentValues;
22d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scottimport android.content.Context;
238188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowskiimport android.content.CursorLoader;
24ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowskiimport android.content.Intent;
25d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scottimport android.database.Cursor;
26d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scottimport android.media.RingtoneManager;
27d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scottimport android.net.Uri;
28d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scottimport android.os.Parcel;
29d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scottimport android.os.Parcelable;
30d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
31e10d62be00a99c19f2a111801d7f80ecb7baf336Annie Chinimport com.android.deskclock.R;
326a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieuximport com.android.deskclock.data.DataModel;
336a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux
34d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scottimport java.util.Calendar;
358188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowskiimport java.util.LinkedList;
368188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowskiimport java.util.List;
37d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
388188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowskipublic final class Alarm implements Parcelable, ClockContract.AlarmsColumns {
398188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    /**
408188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * Alarms start with an invalid id when it hasn't been saved to the database.
418188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     */
428188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public static final long INVALID_ID = -1;
43d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
448188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    /**
458188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * The default sort order for this table
468188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     */
478188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    private static final String DEFAULT_SORT_ORDER =
48381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.ALARMS_TABLE_NAME + "." + HOUR + ", " +
49381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.ALARMS_TABLE_NAME + "." +  MINUTES + " ASC" + ", " +
50381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.ALARMS_TABLE_NAME + "." + ClockContract.AlarmsColumns._ID + " DESC";
518188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
528188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    private static final String[] QUERY_COLUMNS = {
538188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski            _ID,
548188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski            HOUR,
558188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski            MINUTES,
568188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski            DAYS_OF_WEEK,
578188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski            ENABLED,
588188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski            VIBRATE,
59ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski            LABEL,
60ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski            RINGTONE,
612f2584bdcabbf2f114f48efcab7ceb0a34067761Annie Chin            DELETE_AFTER_USE
628188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    };
638188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
64381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    private static final String[] QUERY_ALARMS_WITH_INSTANCES_COLUMNS = {
65381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.ALARMS_TABLE_NAME + "." + _ID,
66381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.ALARMS_TABLE_NAME + "." + HOUR,
67381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.ALARMS_TABLE_NAME + "." + MINUTES,
68381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.ALARMS_TABLE_NAME + "." + DAYS_OF_WEEK,
69381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.ALARMS_TABLE_NAME + "." + ENABLED,
70381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.ALARMS_TABLE_NAME + "." + VIBRATE,
71381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.ALARMS_TABLE_NAME + "." + LABEL,
72381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.ALARMS_TABLE_NAME + "." + RINGTONE,
73381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.ALARMS_TABLE_NAME + "." + DELETE_AFTER_USE,
74381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.INSTANCES_TABLE_NAME + "."
75381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin                    + ClockContract.InstancesColumns.ALARM_STATE,
76381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.INSTANCES_TABLE_NAME + "." + ClockContract.InstancesColumns._ID,
77381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.INSTANCES_TABLE_NAME + "." + ClockContract.InstancesColumns.YEAR,
78381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.INSTANCES_TABLE_NAME + "." + ClockContract.InstancesColumns.MONTH,
79381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.INSTANCES_TABLE_NAME + "." + ClockContract.InstancesColumns.DAY,
80381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.INSTANCES_TABLE_NAME + "." + ClockContract.InstancesColumns.HOUR,
81381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.INSTANCES_TABLE_NAME + "." + ClockContract.InstancesColumns.MINUTES,
82381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.INSTANCES_TABLE_NAME + "." + ClockContract.InstancesColumns.LABEL,
83381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            ClockDatabaseHelper.INSTANCES_TABLE_NAME + "." + ClockContract.InstancesColumns.VIBRATE
84381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    };
85381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin
868188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    /**
878188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * These save calls to cursor.getColumnIndexOrThrow()
888188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * THEY MUST BE KEPT IN SYNC WITH ABOVE QUERY COLUMNS
898188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     */
908188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    private static final int ID_INDEX = 0;
918188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    private static final int HOUR_INDEX = 1;
928188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    private static final int MINUTES_INDEX = 2;
938188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    private static final int DAYS_OF_WEEK_INDEX = 3;
94ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    private static final int ENABLED_INDEX = 4;
95ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    private static final int VIBRATE_INDEX = 5;
96ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    private static final int LABEL_INDEX = 6;
97ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    private static final int RINGTONE_INDEX = 7;
98ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    private static final int DELETE_AFTER_USE_INDEX = 8;
99381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    private static final int INSTANCE_STATE_INDEX = 9;
100381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    public static final int INSTANCE_ID_INDEX = 10;
101381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    public static final int INSTANCE_YEAR_INDEX = 11;
102381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    public static final int INSTANCE_MONTH_INDEX = 12;
103381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    public static final int INSTANCE_DAY_INDEX = 13;
104381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    public static final int INSTANCE_HOUR_INDEX = 14;
105381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    public static final int INSTANCE_MINUTE_INDEX = 15;
106381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    public static final int INSTANCE_LABEL_INDEX = 16;
107381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    public static final int INSTANCE_VIBRATE_INDEX = 17;
1088188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
1092f2584bdcabbf2f114f48efcab7ceb0a34067761Annie Chin    private static final int COLUMN_COUNT = DELETE_AFTER_USE_INDEX + 1;
110381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    private static final int ALARM_JOIN_INSTANCE_COLUMN_COUNT = INSTANCE_VIBRATE_INDEX + 1;
1118188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
1128188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public static ContentValues createContentValues(Alarm alarm) {
1138188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        ContentValues values = new ContentValues(COLUMN_COUNT);
1148188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        if (alarm.id != INVALID_ID) {
1158188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski            values.put(ClockContract.AlarmsColumns._ID, alarm.id);
1168188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        }
1178188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
1188188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        values.put(ENABLED, alarm.enabled ? 1 : 0);
1198188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        values.put(HOUR, alarm.hour);
1208188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        values.put(MINUTES, alarm.minutes);
1218188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        values.put(DAYS_OF_WEEK, alarm.daysOfWeek.getBitSet());
12220dccf1991285ab46670521cea5adbc795b5be5ePaul Sliwowski        values.put(VIBRATE, alarm.vibrate ? 1 : 0);
123ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        values.put(LABEL, alarm.label);
1248188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        values.put(DELETE_AFTER_USE, alarm.deleteAfterUse);
125ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        if (alarm.alert == null) {
126ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski            // We want to put null, so default alarm changes
127ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski            values.putNull(RINGTONE);
128ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        } else {
129ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski            values.put(RINGTONE, alarm.alert.toString());
130ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        }
1318188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
1328188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        return values;
133d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    }
134d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
135ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    public static Intent createIntent(Context context, Class<?> cls, long alarmId) {
136ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        return new Intent(context, cls).setData(getUri(alarmId));
137ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    }
138ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski
139ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    public static Uri getUri(long alarmId) {
140ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        return ContentUris.withAppendedId(CONTENT_URI, alarmId);
141ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    }
142ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski
1438188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public static long getId(Uri contentUri) {
1448188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        return ContentUris.parseId(contentUri);
145d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    }
1469fc42f9af42b408e85655c55e6c7825bc1ce8665Paul Sliwowski
1478188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    /**
1488188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * Get alarm cursor loader for all alarms.
1498188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     *
1508188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * @param context to query the database.
1518188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * @return cursor loader with all the alarms.
1528188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     */
1538188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public static CursorLoader getAlarmsCursorLoader(Context context) {
154381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin        return new CursorLoader(context, ALARMS_WITH_INSTANCES_URI,
155381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin                QUERY_ALARMS_WITH_INSTANCES_COLUMNS, null, null, DEFAULT_SORT_ORDER);
156d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    }
1578188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
1588188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    /**
1598188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * Get alarm by id.
1608188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     *
1619c1f508c1f68bbbcb8fc42f8fc7748d63833fe89James Lemieux     * @param cr to perform the query on.
1628188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * @param alarmId for the desired alarm.
1638188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * @return alarm if found, null otherwise
1648188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     */
1659c1f508c1f68bbbcb8fc42f8fc7748d63833fe89James Lemieux    public static Alarm getAlarm(ContentResolver cr, long alarmId) {
1669c1f508c1f68bbbcb8fc42f8fc7748d63833fe89James Lemieux        try (Cursor cursor = cr.query(getUri(alarmId), QUERY_COLUMNS, null, null, null)) {
1678188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski            if (cursor.moveToFirst()) {
1689c1f508c1f68bbbcb8fc42f8fc7748d63833fe89James Lemieux                return new Alarm(cursor);
1698188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski            }
1708188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        }
1718188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
1729c1f508c1f68bbbcb8fc42f8fc7748d63833fe89James Lemieux        return null;
1738188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    }
1748188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
1758188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    /**
1768188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * Get all alarms given conditions.
1778188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     *
1789c1f508c1f68bbbcb8fc42f8fc7748d63833fe89James Lemieux     * @param cr to perform the query on.
1798188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * @param selection A filter declaring which rows to return, formatted as an
1808188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     *         SQL WHERE clause (excluding the WHERE itself). Passing null will
1818188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     *         return all rows for the given URI.
1828188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * @param selectionArgs You may include ?s in selection, which will be
1838188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     *         replaced by the values from selectionArgs, in the order that they
1848188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     *         appear in the selection. The values will be bound as Strings.
1858188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     * @return list of alarms matching where clause or empty list if none found.
1868188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski     */
1879c1f508c1f68bbbcb8fc42f8fc7748d63833fe89James Lemieux    public static List<Alarm> getAlarms(ContentResolver cr, String selection,
1889c1f508c1f68bbbcb8fc42f8fc7748d63833fe89James Lemieux            String... selectionArgs) {
1899c1f508c1f68bbbcb8fc42f8fc7748d63833fe89James Lemieux        final List<Alarm> result = new LinkedList<>();
1909c1f508c1f68bbbcb8fc42f8fc7748d63833fe89James Lemieux        try (Cursor cursor = cr.query(CONTENT_URI, QUERY_COLUMNS, selection, selectionArgs, null)) {
1919c1f508c1f68bbbcb8fc42f8fc7748d63833fe89James Lemieux            if (cursor != null && cursor.moveToFirst()) {
1928188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski                do {
1938188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski                    result.add(new Alarm(cursor));
1948188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski                } while (cursor.moveToNext());
1958188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski            }
1968188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        }
1978188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
1988188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        return result;
1998188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    }
2008188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
201e28d63671ccf81607dc47fa87b7abcf66ef22b86Annie Chin    public static boolean isTomorrow(Alarm alarm, Calendar now) {
202e6f73547d9fed9f9e0a44e6baf2894d317e6ef78Daria Evdokimova        final int alarmHour = alarm.hour;
203e6f73547d9fed9f9e0a44e6baf2894d317e6ef78Daria Evdokimova        final int currHour = now.get(Calendar.HOUR_OF_DAY);
204e28d63671ccf81607dc47fa87b7abcf66ef22b86Annie Chin        // If the alarm is not snoozed and the time is less than the current time, it must be
205e28d63671ccf81607dc47fa87b7abcf66ef22b86Annie Chin        // firing tomorrow.
206e28d63671ccf81607dc47fa87b7abcf66ef22b86Annie Chin        // If the alarm is snoozed, return "false" to indicate that this alarm is firing today.
207e28d63671ccf81607dc47fa87b7abcf66ef22b86Annie Chin        // (The alarm must have already rung today in order to be snoozed, and this function is only
208e28d63671ccf81607dc47fa87b7abcf66ef22b86Annie Chin        // called on non-repeating alarms.)
209e28d63671ccf81607dc47fa87b7abcf66ef22b86Annie Chin        return alarm.instanceState != AlarmInstance.SNOOZE_STATE
210e28d63671ccf81607dc47fa87b7abcf66ef22b86Annie Chin                && (alarmHour < currHour
211e28d63671ccf81607dc47fa87b7abcf66ef22b86Annie Chin                || (alarmHour == currHour && alarm.minutes <= now.get(Calendar.MINUTE)));
212e6f73547d9fed9f9e0a44e6baf2894d317e6ef78Daria Evdokimova    }
213e6f73547d9fed9f9e0a44e6baf2894d317e6ef78Daria Evdokimova
214ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    public static Alarm addAlarm(ContentResolver contentResolver, Alarm alarm) {
215ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        ContentValues values = createContentValues(alarm);
216ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        Uri uri = contentResolver.insert(CONTENT_URI, values);
217ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        alarm.id = getId(uri);
218ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        return alarm;
219ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    }
220ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski
221ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    public static boolean updateAlarm(ContentResolver contentResolver, Alarm alarm) {
2222f2584bdcabbf2f114f48efcab7ceb0a34067761Annie Chin        if (alarm.id == Alarm.INVALID_ID) return false;
223ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        ContentValues values = createContentValues(alarm);
224ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        long rowsUpdated = contentResolver.update(getUri(alarm.id), values, null, null);
225ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        return rowsUpdated == 1;
226ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    }
227ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski
228ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    public static boolean deleteAlarm(ContentResolver contentResolver, long alarmId) {
229ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        if (alarmId == INVALID_ID) return false;
230ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        int deletedRows = contentResolver.delete(getUri(alarmId), "", null);
231ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        return deletedRows == 1;
232ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    }
233ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski
234ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    public static final Parcelable.Creator<Alarm> CREATOR = new Parcelable.Creator<Alarm>() {
2358188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        public Alarm createFromParcel(Parcel p) {
2368188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski            return new Alarm(p);
2378188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        }
2388188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
2398188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        public Alarm[] newArray(int size) {
2408188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski            return new Alarm[size];
2418188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        }
2428188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    };
243d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
244d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    // Public fields
245ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    // TODO: Refactor instance names
2468188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public long id;
2478188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public boolean enabled;
2488188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public int hour;
2498188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public int minutes;
250d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public DaysOfWeek daysOfWeek;
2518188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public boolean vibrate;
2528188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public String label;
2538188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public Uri alert;
2548188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public boolean deleteAfterUse;
255381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    public int instanceState;
256381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin    public int instanceId;
257d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
2588188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    // Creates a default alarm at the current time.
2598188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public Alarm() {
2608188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        this(0, 0);
2618188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    }
2628188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
2638188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public Alarm(int hour, int minutes) {
2648188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        this.id = INVALID_ID;
2658188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        this.hour = hour;
2668188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        this.minutes = minutes;
2678188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        this.vibrate = true;
2688188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        this.daysOfWeek = new DaysOfWeek(0);
2698188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        this.label = "";
2706a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux        this.alert = DataModel.getDataModel().getDefaultAlarmRingtoneUri();
2718188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        this.deleteAfterUse = false;
272dfbd6b43fc22d82b5ba576cb686496bfd8ddc016Chiao Cheng    }
273dfbd6b43fc22d82b5ba576cb686496bfd8ddc016Chiao Cheng
274d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    public Alarm(Cursor c) {
2758188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        id = c.getLong(ID_INDEX);
2768188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        enabled = c.getInt(ENABLED_INDEX) == 1;
2778188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        hour = c.getInt(HOUR_INDEX);
2788188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        minutes = c.getInt(MINUTES_INDEX);
2798188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        daysOfWeek = new DaysOfWeek(c.getInt(DAYS_OF_WEEK_INDEX));
2808188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        vibrate = c.getInt(VIBRATE_INDEX) == 1;
281ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        label = c.getString(LABEL_INDEX);
2828188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        deleteAfterUse = c.getInt(DELETE_AFTER_USE_INDEX) == 1;
2838188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski
284381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin        if (c.getColumnCount() == ALARM_JOIN_INSTANCE_COLUMN_COUNT) {
285381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            instanceState = c.getInt(INSTANCE_STATE_INDEX);
286381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin            instanceId = c.getInt(INSTANCE_ID_INDEX);
287381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin        }
288381f4f17c8b2d4f0aac35eca56a315d129c5fb1eAnnie Chin
289ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        if (c.isNull(RINGTONE_INDEX)) {
290ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski            // Should we be saving this with the current ringtone or leave it null
291ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski            // so it changes when user changes default ringtone?
292ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski            alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
293d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        } else {
294ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski            alert = Uri.parse(c.getString(RINGTONE_INDEX));
295d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        }
296d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    }
297d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
2988188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    Alarm(Parcel p) {
2998188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        id = p.readLong();
300d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        enabled = p.readInt() == 1;
301d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        hour = p.readInt();
302d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        minutes = p.readInt();
303d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        daysOfWeek = new DaysOfWeek(p.readInt());
304d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        vibrate = p.readInt() == 1;
305d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott        label = p.readString();
3066a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux        alert = p.readParcelable(null);
3078188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        deleteAfterUse = p.readInt() == 1;
3089fc42f9af42b408e85655c55e6c7825bc1ce8665Paul Sliwowski    }
3099fc42f9af42b408e85655c55e6c7825bc1ce8665Paul Sliwowski
310e10d62be00a99c19f2a111801d7f80ecb7baf336Annie Chin    public String getLabelOrDefault(Context context) {
311e10d62be00a99c19f2a111801d7f80ecb7baf336Annie Chin        return label.isEmpty() ? context.getString(R.string.default_label) : label;
312e10d62be00a99c19f2a111801d7f80ecb7baf336Annie Chin    }
313e10d62be00a99c19f2a111801d7f80ecb7baf336Annie Chin
314effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    /**
315effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     * Whether the alarm is in a state to show preemptive dismiss. Valid states are SNOOZE_STATE
3164c9234c459245f39a6d991a030027c383dfbf560Annie Chin     * HIGH_NOTIFICATION, LOW_NOTIFICATION, and HIDE_NOTIFICATION.
317effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     */
318effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    public boolean canPreemptivelyDismiss() {
319effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        return instanceState == AlarmInstance.SNOOZE_STATE
320effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                || instanceState == AlarmInstance.HIGH_NOTIFICATION_STATE
3214c9234c459245f39a6d991a030027c383dfbf560Annie Chin                || instanceState == AlarmInstance.LOW_NOTIFICATION_STATE
3224c9234c459245f39a6d991a030027c383dfbf560Annie Chin                || instanceState == AlarmInstance.HIDE_NOTIFICATION_STATE;
323d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    }
324d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott
3258188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public void writeToParcel(Parcel p, int flags) {
3268188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        p.writeLong(id);
3278188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        p.writeInt(enabled ? 1 : 0);
3288188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        p.writeInt(hour);
3298188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        p.writeInt(minutes);
3308188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        p.writeInt(daysOfWeek.getBitSet());
3318188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        p.writeInt(vibrate ? 1 : 0);
3328188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        p.writeString(label);
3338188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        p.writeParcelable(alert, flags);
3348188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        p.writeInt(deleteAfterUse ? 1 : 0);
3358304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook    }
3368304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook
3378188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public int describeContents() {
3388188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        return 0;
3398304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook    }
3408304c6e63ee1ff86208bac6f79285905027e95e8Paul Westbrook
341ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski    public AlarmInstance createInstanceAfter(Calendar time) {
342c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin        Calendar nextInstanceTime = getNextAlarmTime(time);
343c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin        AlarmInstance result = new AlarmInstance(nextInstanceTime, id);
344c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin        result.mVibrate = vibrate;
345c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin        result.mLabel = label;
346c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin        result.mRingtone = alert;
347c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin        return result;
348c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin    }
349c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin
350d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin    /**
351d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin     *
352d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin     * @param currentTime
353d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin     * @return Previous firing time, or null if this is a one-time alarm.
354d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin     */
355d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin    public Calendar getPreviousAlarmTime(Calendar currentTime) {
356d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin        Calendar previousInstanceTime = Calendar.getInstance();
357d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin        previousInstanceTime.set(Calendar.YEAR, currentTime.get(Calendar.YEAR));
358d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin        previousInstanceTime.set(Calendar.MONTH, currentTime.get(Calendar.MONTH));
359d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin        previousInstanceTime.set(Calendar.DAY_OF_MONTH, currentTime.get(Calendar.DAY_OF_MONTH));
360d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin        previousInstanceTime.set(Calendar.HOUR_OF_DAY, hour);
361d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin        previousInstanceTime.set(Calendar.MINUTE, minutes);
362d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin        previousInstanceTime.set(Calendar.SECOND, 0);
363d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin        previousInstanceTime.set(Calendar.MILLISECOND, 0);
364d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin
365d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin        int subtractDays = daysOfWeek.calculateDaysToPreviousAlarm(previousInstanceTime);
366d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin        if (subtractDays > 0) {
367d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin            previousInstanceTime.add(Calendar.DAY_OF_WEEK, -subtractDays);
368d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin            return previousInstanceTime;
369d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin        } else {
370d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin            return null;
371d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin        }
372d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin    }
373d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin
374c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin    public Calendar getNextAlarmTime(Calendar currentTime) {
375d68d58de285756f5094a33b12385f4989c76d9c4Annie Chin        final Calendar nextInstanceTime = Calendar.getInstance();
376c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin        nextInstanceTime.set(Calendar.YEAR, currentTime.get(Calendar.YEAR));
377c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin        nextInstanceTime.set(Calendar.MONTH, currentTime.get(Calendar.MONTH));
378c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin        nextInstanceTime.set(Calendar.DAY_OF_MONTH, currentTime.get(Calendar.DAY_OF_MONTH));
379ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        nextInstanceTime.set(Calendar.HOUR_OF_DAY, hour);
380ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        nextInstanceTime.set(Calendar.MINUTE, minutes);
381ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        nextInstanceTime.set(Calendar.SECOND, 0);
382ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        nextInstanceTime.set(Calendar.MILLISECOND, 0);
383ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski
384c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin        // If we are still behind the passed in currentTime, then add a day
385c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin        if (nextInstanceTime.getTimeInMillis() <= currentTime.getTimeInMillis()) {
386ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski            nextInstanceTime.add(Calendar.DAY_OF_YEAR, 1);
38795b88d9453d7d47026b4a2e3bcc4e7bbb4dd7ccfPaul Sliwowski        }
38895b88d9453d7d47026b4a2e3bcc4e7bbb4dd7ccfPaul Sliwowski
389ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        // The day of the week might be invalid, so find next valid one
390ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski        int addDays = daysOfWeek.calculateDaysToNextAlarm(nextInstanceTime);
39195b88d9453d7d47026b4a2e3bcc4e7bbb4dd7ccfPaul Sliwowski        if (addDays > 0) {
392ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski            nextInstanceTime.add(Calendar.DAY_OF_WEEK, addDays);
39395b88d9453d7d47026b4a2e3bcc4e7bbb4dd7ccfPaul Sliwowski        }
3949699c4ab5817c103f804633ebef2d3c0d5db1f9cJames Lemieux
3959699c4ab5817c103f804633ebef2d3c0d5db1f9cJames Lemieux        // Daylight Savings Time can alter the hours and minutes when adjusting the day above.
3969699c4ab5817c103f804633ebef2d3c0d5db1f9cJames Lemieux        // Reset the desired hour and minute now that the correct day has been chosen.
3979699c4ab5817c103f804633ebef2d3c0d5db1f9cJames Lemieux        nextInstanceTime.set(Calendar.HOUR_OF_DAY, hour);
3989699c4ab5817c103f804633ebef2d3c0d5db1f9cJames Lemieux        nextInstanceTime.set(Calendar.MINUTE, minutes);
3999699c4ab5817c103f804633ebef2d3c0d5db1f9cJames Lemieux
400c34e4a02bf9a8ef294891281fd46dcf8f6dd8d85Annie Chin        return nextInstanceTime;
40195b88d9453d7d47026b4a2e3bcc4e7bbb4dd7ccfPaul Sliwowski    }
40295b88d9453d7d47026b4a2e3bcc4e7bbb4dd7ccfPaul Sliwowski
4038188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    @Override
4048188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public boolean equals(Object o) {
4058188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        if (!(o instanceof Alarm)) return false;
4068188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        final Alarm other = (Alarm) o;
4078188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        return id == other.id;
4088188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    }
409dfbd6b43fc22d82b5ba576cb686496bfd8ddc016Chiao Cheng
4108188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    @Override
4118188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public int hashCode() {
4128188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        return Long.valueOf(id).hashCode();
4138188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    }
41495b88d9453d7d47026b4a2e3bcc4e7bbb4dd7ccfPaul Sliwowski
4158188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    @Override
4168188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski    public String toString() {
4178188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski        return "Alarm{" +
4188188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski                "alert=" + alert +
4198188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski                ", id=" + id +
4208188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski                ", enabled=" + enabled +
4218188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski                ", hour=" + hour +
4228188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski                ", minutes=" + minutes +
4238188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski                ", daysOfWeek=" + daysOfWeek +
4248188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski                ", vibrate=" + vibrate +
4258188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski                ", label='" + label + '\'' +
4268188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski                ", deleteAfterUse=" + deleteAfterUse +
4278188813bc869d3df4885f9c2972f9cc85745b59bPaul Sliwowski                '}';
428d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott    }
429d776e51cec79901a0d656bba7b1f3780cceac57dPatrick Scott}
430