DataModel.java revision 58d9315aed4f645eb60c22be117b074e18c0982f
134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux/*
234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux * Copyright (C) 2015 The Android Open Source Project
334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux *
434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux * Licensed under the Apache License, Version 2.0 (the "License");
534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux * you may not use this file except in compliance with the License.
634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux * You may obtain a copy of the License at
734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux *
834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux *      http://www.apache.org/licenses/LICENSE-2.0
934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux *
1034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux * Unless required by applicable law or agreed to in writing, software
1134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux * distributed under the License is distributed on an "AS IS" BASIS,
1234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux * See the License for the specific language governing permissions and
1434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux * limitations under the License.
1534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux */
1634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
1734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieuxpackage com.android.deskclock.data;
1834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
190dd0cac610cd59762c8b604da6c437b18a29246bJames Lemieuximport android.app.Service;
2034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieuximport android.content.Context;
2125384711e3c2894a84642c49953594930dd078abJames Lemieuximport android.content.Intent;
2223629266834a251cb937a885e5223e5ae37cc6faChristine Franksimport android.content.SharedPreferences;
2325384711e3c2894a84642c49953594930dd078abJames Lemieuximport android.media.AudioManager;
24856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieuximport android.net.Uri;
25d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phanimport android.os.Handler;
26d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phanimport android.os.Looper;
276d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.support.annotation.StringRes;
2825384711e3c2894a84642c49953594930dd078abJames Lemieuximport android.view.View;
2934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
3025384711e3c2894a84642c49953594930dd078abJames Lemieuximport com.android.deskclock.R;
3125384711e3c2894a84642c49953594930dd078abJames Lemieuximport com.android.deskclock.Utils;
320a3313e231702cc9944e9a17e52aea62eb25afabSean Stoutimport com.android.deskclock.timer.TimerService;
330a3313e231702cc9944e9a17e52aea62eb25afabSean Stout
34458aa8b4ebb8b7c6fdc0680a1b687ea21a61bf35James Lemieuximport java.util.Calendar;
3534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieuximport java.util.Collection;
3634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieuximport java.util.Comparator;
3734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieuximport java.util.List;
3834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
3925384711e3c2894a84642c49953594930dd078abJames Lemieuximport static android.content.Context.AUDIO_SERVICE;
4025384711e3c2894a84642c49953594930dd078abJames Lemieuximport static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
4125384711e3c2894a84642c49953594930dd078abJames Lemieuximport static android.media.AudioManager.FLAG_SHOW_UI;
4225384711e3c2894a84642c49953594930dd078abJames Lemieuximport static android.media.AudioManager.STREAM_ALARM;
4325384711e3c2894a84642c49953594930dd078abJames Lemieuximport static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS;
4425384711e3c2894a84642c49953594930dd078abJames Lemieuximport static android.provider.Settings.ACTION_SOUND_SETTINGS;
4534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieuximport static com.android.deskclock.Utils.enforceMainLooper;
46c9446b52d9391e859e65d11e5342d84cfe3e781cJames Lemieuximport static com.android.deskclock.Utils.enforceNotMainLooper;
4734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
4834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux/**
4934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux * All application-wide data is accessible through this singleton.
5034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux */
5134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieuxpublic final class DataModel {
5234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
5334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /** Indicates the display style of clocks. */
5434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public enum ClockStyle {ANALOG, DIGITAL}
5534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
5634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /** Indicates the preferred sort order of cities. */
5734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public enum CitySort {NAME, UTC_OFFSET}
5834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
590777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    /** Indicates the preferred behavior of hardware volume buttons when firing alarms. */
600777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    public enum AlarmVolumeButtonBehavior {NOTHING, SNOOZE, DISMISS}
610777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux
6225384711e3c2894a84642c49953594930dd078abJames Lemieux    /** Indicates the reason alarms may not fire or may fire silently. */
6325384711e3c2894a84642c49953594930dd078abJames Lemieux    public enum SilentSetting {
6425384711e3c2894a84642c49953594930dd078abJames Lemieux        DO_NOT_DISTURB(R.string.alarms_blocked_by_dnd, 0, null),
6525384711e3c2894a84642c49953594930dd078abJames Lemieux        MUTED_VOLUME(R.string.alarm_volume_muted,
6625384711e3c2894a84642c49953594930dd078abJames Lemieux                R.string.unmute_alarm_volume,
6725384711e3c2894a84642c49953594930dd078abJames Lemieux                new UnmuteAlarmVolumeListener()),
6825384711e3c2894a84642c49953594930dd078abJames Lemieux        SILENT_RINGTONE(R.string.silent_default_alarm_ringtone,
6925384711e3c2894a84642c49953594930dd078abJames Lemieux                R.string.change_setting_action,
7025384711e3c2894a84642c49953594930dd078abJames Lemieux                new ChangeSoundSettingsListener()),
7125384711e3c2894a84642c49953594930dd078abJames Lemieux        BLOCKED_NOTIFICATIONS(R.string.app_notifications_blocked,
7225384711e3c2894a84642c49953594930dd078abJames Lemieux                R.string.change_setting_action,
7325384711e3c2894a84642c49953594930dd078abJames Lemieux                new ChangeAppNotificationSettingsListener());
7425384711e3c2894a84642c49953594930dd078abJames Lemieux
7525384711e3c2894a84642c49953594930dd078abJames Lemieux        private final @StringRes int mLabelResId;
7625384711e3c2894a84642c49953594930dd078abJames Lemieux        private final @StringRes int mActionResId;
7725384711e3c2894a84642c49953594930dd078abJames Lemieux        private final View.OnClickListener mActionListener;
7825384711e3c2894a84642c49953594930dd078abJames Lemieux
7925384711e3c2894a84642c49953594930dd078abJames Lemieux        SilentSetting(int labelResId, int actionResId, View.OnClickListener actionListener) {
8025384711e3c2894a84642c49953594930dd078abJames Lemieux            mLabelResId = labelResId;
8125384711e3c2894a84642c49953594930dd078abJames Lemieux            mActionResId = actionResId;
8225384711e3c2894a84642c49953594930dd078abJames Lemieux            mActionListener = actionListener;
8325384711e3c2894a84642c49953594930dd078abJames Lemieux        }
8425384711e3c2894a84642c49953594930dd078abJames Lemieux
8525384711e3c2894a84642c49953594930dd078abJames Lemieux        public @StringRes int getLabelResId() { return mLabelResId; }
8625384711e3c2894a84642c49953594930dd078abJames Lemieux        public @StringRes int getActionResId() { return mActionResId; }
8725384711e3c2894a84642c49953594930dd078abJames Lemieux        public View.OnClickListener getActionListener() { return mActionListener; }
8825384711e3c2894a84642c49953594930dd078abJames Lemieux
8925384711e3c2894a84642c49953594930dd078abJames Lemieux        private static class UnmuteAlarmVolumeListener implements View.OnClickListener {
9025384711e3c2894a84642c49953594930dd078abJames Lemieux            @Override
9125384711e3c2894a84642c49953594930dd078abJames Lemieux            public void onClick(View v) {
9225384711e3c2894a84642c49953594930dd078abJames Lemieux                // Set the alarm volume to ~30% of max and show the slider UI.
9325384711e3c2894a84642c49953594930dd078abJames Lemieux                final Context context = v.getContext();
9425384711e3c2894a84642c49953594930dd078abJames Lemieux                final AudioManager am = (AudioManager) context.getSystemService(AUDIO_SERVICE);
9525384711e3c2894a84642c49953594930dd078abJames Lemieux                final int index = am.getStreamMaxVolume(STREAM_ALARM) / 3;
9625384711e3c2894a84642c49953594930dd078abJames Lemieux                am.setStreamVolume(STREAM_ALARM, index, FLAG_SHOW_UI);
9725384711e3c2894a84642c49953594930dd078abJames Lemieux            }
98ff62e7fa903e3b6b11d0443543725c1351ab289dJames Lemieux        }
9925384711e3c2894a84642c49953594930dd078abJames Lemieux
10025384711e3c2894a84642c49953594930dd078abJames Lemieux        private static class ChangeSoundSettingsListener implements View.OnClickListener {
10125384711e3c2894a84642c49953594930dd078abJames Lemieux            @Override
10225384711e3c2894a84642c49953594930dd078abJames Lemieux            public void onClick(View v) {
10325384711e3c2894a84642c49953594930dd078abJames Lemieux                final Context context = v.getContext();
10425384711e3c2894a84642c49953594930dd078abJames Lemieux                context.startActivity(new Intent(ACTION_SOUND_SETTINGS)
10525384711e3c2894a84642c49953594930dd078abJames Lemieux                        .addFlags(FLAG_ACTIVITY_NEW_TASK));
10625384711e3c2894a84642c49953594930dd078abJames Lemieux            }
107ff62e7fa903e3b6b11d0443543725c1351ab289dJames Lemieux        }
10825384711e3c2894a84642c49953594930dd078abJames Lemieux
10925384711e3c2894a84642c49953594930dd078abJames Lemieux        private static class ChangeAppNotificationSettingsListener implements View.OnClickListener {
11025384711e3c2894a84642c49953594930dd078abJames Lemieux            @Override
11125384711e3c2894a84642c49953594930dd078abJames Lemieux            public void onClick(View v) {
11225384711e3c2894a84642c49953594930dd078abJames Lemieux                final Context context = v.getContext();
11325384711e3c2894a84642c49953594930dd078abJames Lemieux                if (Utils.isLOrLater()) {
11425384711e3c2894a84642c49953594930dd078abJames Lemieux                    try {
11525384711e3c2894a84642c49953594930dd078abJames Lemieux                        // Attempt to open the notification settings for this app.
11625384711e3c2894a84642c49953594930dd078abJames Lemieux                        context.startActivity(
11725384711e3c2894a84642c49953594930dd078abJames Lemieux                                new Intent("android.settings.APP_NOTIFICATION_SETTINGS")
11825384711e3c2894a84642c49953594930dd078abJames Lemieux                                .putExtra("app_package", context.getPackageName())
11925384711e3c2894a84642c49953594930dd078abJames Lemieux                                .putExtra("app_uid", context.getApplicationInfo().uid)
12025384711e3c2894a84642c49953594930dd078abJames Lemieux                                .addFlags(FLAG_ACTIVITY_NEW_TASK));
12125384711e3c2894a84642c49953594930dd078abJames Lemieux                        return;
12225384711e3c2894a84642c49953594930dd078abJames Lemieux                    } catch (Exception ignored) {
12325384711e3c2894a84642c49953594930dd078abJames Lemieux                        // best attempt only; recovery code below
12425384711e3c2894a84642c49953594930dd078abJames Lemieux                    }
12525384711e3c2894a84642c49953594930dd078abJames Lemieux                }
12625384711e3c2894a84642c49953594930dd078abJames Lemieux
12725384711e3c2894a84642c49953594930dd078abJames Lemieux                // Fall back to opening the app settings page.
12825384711e3c2894a84642c49953594930dd078abJames Lemieux                context.startActivity(new Intent(ACTION_APPLICATION_DETAILS_SETTINGS)
12925384711e3c2894a84642c49953594930dd078abJames Lemieux                        .setData(Uri.fromParts("package", context.getPackageName(), null))
13025384711e3c2894a84642c49953594930dd078abJames Lemieux                        .addFlags(FLAG_ACTIVITY_NEW_TASK));
13125384711e3c2894a84642c49953594930dd078abJames Lemieux            }
132ff62e7fa903e3b6b11d0443543725c1351ab289dJames Lemieux        }
13325384711e3c2894a84642c49953594930dd078abJames Lemieux    }
13425384711e3c2894a84642c49953594930dd078abJames Lemieux
1358bf1c3ba34724773b92242effec8330e1852bb7bJames Lemieux    public static final String ACTION_WORLD_CITIES_CHANGED =
1368bf1c3ba34724773b92242effec8330e1852bb7bJames Lemieux            "com.android.deskclock.WORLD_CITIES_CHANGED";
1378bf1c3ba34724773b92242effec8330e1852bb7bJames Lemieux
13834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /** The single instance of this data model that exists for the life of the application. */
13934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    private static final DataModel sDataModel = new DataModel();
14034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
141d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    private Handler mHandler;
142d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
14334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    private Context mContext;
14434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
14534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /** The model from which settings are fetched. */
14634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    private SettingsModel mSettingsModel;
14734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
148856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    /** The model from which city data are fetched. */
14934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    private CityModel mCityModel;
15034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
151856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    /** The model from which timer data are fetched. */
152856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    private TimerModel mTimerModel;
153856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux
1546a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    /** The model from which alarm data are fetched. */
1556a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    private AlarmModel mAlarmModel;
1566a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux
157592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    /** The model from which widget data are fetched. */
158592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    private WidgetModel mWidgetModel;
159592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux
16025384711e3c2894a84642c49953594930dd078abJames Lemieux    /** The model from which data about settings that silence alarms are fetched. */
16125384711e3c2894a84642c49953594930dd078abJames Lemieux    private SilentSettingsModel mSilentSettingsModel;
16225384711e3c2894a84642c49953594930dd078abJames Lemieux
16324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /** The model from which stopwatch data are fetched. */
16424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    private StopwatchModel mStopwatchModel;
16524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
16624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /** The model from which notification data are fetched. */
16724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    private NotificationModel mNotificationModel;
16824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
169be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout    /** The model from which time data are fetched. */
170be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout    private TimeModel mTimeModel;
171be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout
17233781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    /** The model from which ringtone data are fetched. */
17333781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    private RingtoneModel mRingtoneModel;
17433781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux
17534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public static DataModel getDataModel() {
17634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return sDataModel;
17734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
17834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
17934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    private DataModel() {}
18034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
18134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
182ff62e7fa903e3b6b11d0443543725c1351ab289dJames Lemieux     * Initializes the data model with the context and shared preferences to be used.
18334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
184ff62e7fa903e3b6b11d0443543725c1351ab289dJames Lemieux    public void init(Context context, SharedPreferences prefs) {
185310f96ed002219067d2e652e27d64d256e315832James Lemieux        if (mContext != context) {
186310f96ed002219067d2e652e27d64d256e315832James Lemieux            mContext = context.getApplicationContext();
187310f96ed002219067d2e652e27d64d256e315832James Lemieux
1880b19985ecfa0f43acb8ededc173f643843098a95James Lemieux            mTimeModel = new TimeModel(mContext);
189ff62e7fa903e3b6b11d0443543725c1351ab289dJames Lemieux            mWidgetModel = new WidgetModel(prefs);
190ff62e7fa903e3b6b11d0443543725c1351ab289dJames Lemieux            mSettingsModel = new SettingsModel(mContext, prefs);
191ff62e7fa903e3b6b11d0443543725c1351ab289dJames Lemieux            mRingtoneModel = new RingtoneModel(mContext, prefs);
192310f96ed002219067d2e652e27d64d256e315832James Lemieux            mNotificationModel = new NotificationModel();
193ff62e7fa903e3b6b11d0443543725c1351ab289dJames Lemieux            mCityModel = new CityModel(mContext, prefs, mSettingsModel);
194310f96ed002219067d2e652e27d64d256e315832James Lemieux            mAlarmModel = new AlarmModel(mContext, mSettingsModel);
19525384711e3c2894a84642c49953594930dd078abJames Lemieux            mSilentSettingsModel = new SilentSettingsModel(mContext, mNotificationModel);
196ff62e7fa903e3b6b11d0443543725c1351ab289dJames Lemieux            mStopwatchModel = new StopwatchModel(mContext, prefs, mNotificationModel);
197ff62e7fa903e3b6b11d0443543725c1351ab289dJames Lemieux            mTimerModel = new TimerModel(mContext, prefs, mSettingsModel, mRingtoneModel,
198ff62e7fa903e3b6b11d0443543725c1351ab289dJames Lemieux                    mNotificationModel);
19923629266834a251cb937a885e5223e5ae37cc6faChristine Franks        }
20023629266834a251cb937a885e5223e5ae37cc6faChristine Franks    }
20123629266834a251cb937a885e5223e5ae37cc6faChristine Franks
20223629266834a251cb937a885e5223e5ae37cc6faChristine Franks    /**
203a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen     * Convenience for {@code run(runnable, 0)}, i.e. waits indefinitely.
204a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen     */
205a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen    public void run(Runnable runnable) {
206a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen        try {
207a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen            run(runnable, 0 /* waitMillis */);
208a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen        } catch (InterruptedException ignored) {
209a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen        }
210a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen    }
211a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen
212a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen    /**
2132a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     * Updates all timers and the stopwatch after the device has shutdown and restarted.
2142a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     */
2152a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    public void updateAfterReboot() {
2162a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        enforceMainLooper();
2172a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mTimerModel.updateTimersAfterReboot();
2182a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mStopwatchModel.setStopwatch(getStopwatch().updateAfterReboot());
2192a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    }
2202a07ae3286fd5c76f71546890e0f02af99065825Sean Stout
2212a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    /**
2222a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     * Updates all timers and the stopwatch after the device's time has changed.
2232a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     */
2242a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    public void updateAfterTimeSet() {
2252a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        enforceMainLooper();
2262a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mTimerModel.updateTimersAfterTimeSet();
2272a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mStopwatchModel.setStopwatch(getStopwatch().updateAfterTimeSet());
2282a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    }
2292a07ae3286fd5c76f71546890e0f02af99065825Sean Stout
2302a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    /**
231d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     * Posts a runnable to the main thread and blocks until the runnable executes. Used to access
232d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     * the data model from the main thread.
233d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     */
234a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen    public void run(Runnable runnable, long waitMillis) throws InterruptedException {
235d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        if (Looper.myLooper() == Looper.getMainLooper()) {
236d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            runnable.run();
237d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            return;
238d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        }
239d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
240d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        final ExecutedRunnable er = new ExecutedRunnable(runnable);
241d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        getHandler().post(er);
242d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
243d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        // Wait for the data to arrive, if it has not.
244d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        synchronized (er) {
245d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            if (!er.isExecuted()) {
246a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen                er.wait(waitMillis);
247d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            }
248d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        }
249d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    }
250d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
251d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    /**
252d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     * @return a handler associated with the main thread
253d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     */
254d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    private synchronized Handler getHandler() {
255d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        if (mHandler == null) {
256d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            mHandler = new Handler(Looper.getMainLooper());
257d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        }
258d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        return mHandler;
259d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    }
260d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
26124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    //
26224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    // Application
26324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    //
26424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
26524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
26624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @param inForeground {@code true} to indicate the application is open in the foreground
26724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
26824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public void setApplicationInForeground(boolean inForeground) {
26924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
27024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
27124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        if (mNotificationModel.isApplicationInForeground() != inForeground) {
27224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux            mNotificationModel.setApplicationInForeground(inForeground);
27324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
27424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux            // Refresh all notifications in response to a change in app open state.
2756d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            mTimerModel.updateNotification();
2762a07ae3286fd5c76f71546890e0f02af99065825Sean Stout            mTimerModel.updateMissedNotification();
27724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux            mStopwatchModel.updateNotification();
27825384711e3c2894a84642c49953594930dd078abJames Lemieux            mSilentSettingsModel.updateSilentState();
27924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        }
28024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
28124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
28224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
28324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return {@code true} when the application is open in the foreground; {@code false} otherwise
28424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
28524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public boolean isApplicationInForeground() {
2863101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan        enforceMainLooper();
28724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mNotificationModel.isApplicationInForeground();
28834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
28934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
290ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux    /**
291ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux     * Called when the notifications may be stale or absent from the notification manager and must
292ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux     * be rebuilt. e.g. after upgrading the application
293ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux     */
294ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux    public void updateAllNotifications() {
295376b0f51995803a93d7a6907f8ed30aff06e2aaeJames Lemieux        enforceMainLooper();
296ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux        mTimerModel.updateNotification();
2972a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mTimerModel.updateMissedNotification();
298ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux        mStopwatchModel.updateNotification();
299ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux    }
300ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux
30134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    //
30234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    // Cities
30334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    //
30434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
30534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
30634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return a list of all cities in their display order
30734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
30834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public List<City> getAllCities() {
30934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
31034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mCityModel.getAllCities();
31134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
31234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
31334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
31434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return a city representing the user's home timezone
31534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
31634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public City getHomeCity() {
31734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
31834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mCityModel.getHomeCity();
31934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
32034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
32134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
32234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return a list of cities not selected for display
32334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
32434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public List<City> getUnselectedCities() {
32534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
32634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mCityModel.getUnselectedCities();
32734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
32834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
32934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
33034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return a list of cities selected for display
33134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
33234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public List<City> getSelectedCities() {
33334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
33434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mCityModel.getSelectedCities();
33534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
33634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
33734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
33834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @param cities the new collection of cities selected for display by the user
33934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
34034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public void setSelectedCities(Collection<City> cities) {
34134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
34234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        mCityModel.setSelectedCities(cities);
34334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
34434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
34534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
34634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return a comparator used to locate index positions
34734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
34834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public Comparator<City> getCityIndexComparator() {
34934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
35034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mCityModel.getCityIndexComparator();
35134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
35234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
35334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
35434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return the order in which cities are sorted
35534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
35634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public CitySort getCitySort() {
35734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
35834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mCityModel.getCitySort();
35934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
36034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
36134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
36234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * Adjust the order in which cities are sorted.
36334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
36434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public void toggleCitySort() {
36534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
36634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        mCityModel.toggleCitySort();
36734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
36834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
369a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout    /**
370a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout     * @param cityListener listener to be notified when the world city list changes
371a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout     */
372a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout    public void addCityListener(CityListener cityListener) {
373a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout        enforceMainLooper();
374a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout        mCityModel.addCityListener(cityListener);
375a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout    }
376a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout
377a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout    /**
378a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout     * @param cityListener listener that no longer needs to be notified of world city list changes
379a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout     */
380a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout    public void removeCityListener(CityListener cityListener) {
381a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout        enforceMainLooper();
382a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout        mCityModel.removeCityListener(cityListener);
383a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout    }
384a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout
38534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    //
386856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    // Timers
387856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    //
388856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux
389856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    /**
3906d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timerListener to be notified when timers are added, updated and removed
3916d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
3926d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void addTimerListener(TimerListener timerListener) {
3936d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
3946d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.addTimerListener(timerListener);
3956d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
3966d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
3976d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
3986d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timerListener to no longer be notified when timers are added, updated and removed
3996d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4006d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void removeTimerListener(TimerListener timerListener) {
4016d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4026d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.removeTimerListener(timerListener);
4036d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4046d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4056d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4066d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @return a list of timers for display
4076d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4086d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public List<Timer> getTimers() {
4096d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4106d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return mTimerModel.getTimers();
4116d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4126d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4136d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4146d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @return a list of expired timers for display
4156d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4166d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public List<Timer> getExpiredTimers() {
4176d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4186d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return mTimerModel.getExpiredTimers();
4196d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4206d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4216d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4226d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timerId identifies the timer to return
4236d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @return the timer with the given {@code timerId}
4246d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4256d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public Timer getTimer(int timerId) {
4266d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4276d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return mTimerModel.getTimer(timerId);
4286d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4296d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4306d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4316d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @return the timer that last expired and is still expired now; {@code null} if no timers are
4326d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     *      expired
4336d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4346d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public Timer getMostRecentExpiredTimer() {
4356d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4366d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return mTimerModel.getMostRecentExpiredTimer();
4376d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4386d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4396d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4406d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param length the length of the timer in milliseconds
4416d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param label describes the purpose of the timer
442437da3b08ce9ce1b32f4e544816cb3431ceb8d4eJames Lemieux     * @param deleteAfterUse {@code true} indicates the timer should be deleted when it is reset
4436d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @return the newly added timer
4446d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
445437da3b08ce9ce1b32f4e544816cb3431ceb8d4eJames Lemieux    public Timer addTimer(long length, String label, boolean deleteAfterUse) {
4466d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
447437da3b08ce9ce1b32f4e544816cb3431ceb8d4eJames Lemieux        return mTimerModel.addTimer(length, label, deleteAfterUse);
4486d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4496d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4506d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4516d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to be removed
4526d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4536d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void removeTimer(Timer timer) {
4546d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4556d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.removeTimer(timer);
4566d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4576d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4586d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4596d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to be started
4606d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4616d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void startTimer(Timer timer) {
4620a3313e231702cc9944e9a17e52aea62eb25afabSean Stout        startTimer(null, timer);
4630a3313e231702cc9944e9a17e52aea62eb25afabSean Stout    }
4640a3313e231702cc9944e9a17e52aea62eb25afabSean Stout
4650a3313e231702cc9944e9a17e52aea62eb25afabSean Stout    /**
4660a3313e231702cc9944e9a17e52aea62eb25afabSean Stout     * @param service used to start foreground notifications for expired timers
4670a3313e231702cc9944e9a17e52aea62eb25afabSean Stout     * @param timer the timer to be started
4680a3313e231702cc9944e9a17e52aea62eb25afabSean Stout     */
4690a3313e231702cc9944e9a17e52aea62eb25afabSean Stout    public void startTimer(Service service, Timer timer) {
4706d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4710a3313e231702cc9944e9a17e52aea62eb25afabSean Stout        final Timer started = timer.start();
4720a3313e231702cc9944e9a17e52aea62eb25afabSean Stout        mTimerModel.updateTimer(started);
4730a3313e231702cc9944e9a17e52aea62eb25afabSean Stout        if (timer.getRemainingTime() <= 0) {
4740a3313e231702cc9944e9a17e52aea62eb25afabSean Stout            if (service != null) {
4750a3313e231702cc9944e9a17e52aea62eb25afabSean Stout                expireTimer(service, started);
4760a3313e231702cc9944e9a17e52aea62eb25afabSean Stout            } else {
4770a3313e231702cc9944e9a17e52aea62eb25afabSean Stout                mContext.startService(TimerService.createTimerExpiredIntent(mContext, started));
4780a3313e231702cc9944e9a17e52aea62eb25afabSean Stout            }
4790a3313e231702cc9944e9a17e52aea62eb25afabSean Stout        }
4806d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4816d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4826d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4836d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to be paused
4846d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4856d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void pauseTimer(Timer timer) {
4866d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4876d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.updateTimer(timer.pause());
4886d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4896d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4906d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4910dd0cac610cd59762c8b604da6c437b18a29246bJames Lemieux     * @param service used to start foreground notifications for expired timers
4926d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to be expired
4936d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4940dd0cac610cd59762c8b604da6c437b18a29246bJames Lemieux    public void expireTimer(Service service, Timer timer) {
4956d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4960dd0cac610cd59762c8b604da6c437b18a29246bJames Lemieux        mTimerModel.expireTimer(service, timer);
4976d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4986d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4996d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
50012540ba3d8802d36c3893c6b88300c6a391d7d22Justin Klaassen     * @param timer the timer to be reset
50112540ba3d8802d36c3893c6b88300c6a391d7d22Justin Klaassen     * @return the reset {@code timer}
50212540ba3d8802d36c3893c6b88300c6a391d7d22Justin Klaassen     */
50312540ba3d8802d36c3893c6b88300c6a391d7d22Justin Klaassen    public Timer resetTimer(Timer timer) {
50412540ba3d8802d36c3893c6b88300c6a391d7d22Justin Klaassen        enforceMainLooper();
50512540ba3d8802d36c3893c6b88300c6a391d7d22Justin Klaassen        return mTimerModel.resetTimer(timer, false /* allowDelete */, 0 /* eventLabelId */);
50612540ba3d8802d36c3893c6b88300c6a391d7d22Justin Klaassen    }
50712540ba3d8802d36c3893c6b88300c6a391d7d22Justin Klaassen
50812540ba3d8802d36c3893c6b88300c6a391d7d22Justin Klaassen    /**
5096d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * If the given {@code timer} is expired and marked for deletion after use then this method
5106d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * removes the the timer. The timer is otherwise transitioned to the reset state and continues
5116d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * to exist.
5126d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     *
5136d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to be reset
5146d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
515b10e1abe917de457e9cb3be12ffd5d2634d8f79cJames Lemieux     * @return the reset {@code timer} or {@code null} if the timer was deleted
5166d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
517b10e1abe917de457e9cb3be12ffd5d2634d8f79cJames Lemieux    public Timer resetOrDeleteTimer(Timer timer, @StringRes int eventLabelId) {
5186d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
51912540ba3d8802d36c3893c6b88300c6a391d7d22Justin Klaassen        return mTimerModel.resetTimer(timer, true /* allowDelete */, eventLabelId);
5206d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
5216d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
5226d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
5232a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     * Resets all expired timers.
5246d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     *
5256d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
5266d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
5272a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    public void resetExpiredTimers(@StringRes int eventLabelId) {
5286d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
5292a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mTimerModel.resetExpiredTimers(eventLabelId);
5306d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
5316d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
5326d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
5332a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     * Resets all unexpired timers.
5346d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     *
5356d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
5366d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
5372a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    public void resetUnexpiredTimers(@StringRes int eventLabelId) {
5386d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
5392a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mTimerModel.resetUnexpiredTimers(eventLabelId);
5406d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
5416d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
5426d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
5432a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     * Resets all missed timers.
5446d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     *
5456d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
5466d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
5472a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    public void resetMissedTimers(@StringRes int eventLabelId) {
5486d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
5492a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mTimerModel.resetMissedTimers(eventLabelId);
5506d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
5516d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
5526d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
5536d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to which a minute should be added to the remaining time
5546d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
5556d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void addTimerMinute(Timer timer) {
5566d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
5576d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.updateTimer(timer.addMinute());
5586d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
5596d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
5606d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
5616d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to which the new {@code label} belongs
5626d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param label the new label to store for the {@code timer}
5636d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
5646d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void setTimerLabel(Timer timer, String label) {
5656d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
5666d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.updateTimer(timer.setLabel(label));
5676d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
5686d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
5696d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
57058d9315aed4f645eb60c22be117b074e18c0982fJames Lemieux     * @param timer the timer whose {@code length} to change
571705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen     * @param length the new length of the timer in milliseconds
572705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen     */
573705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen    public void setTimerLength(Timer timer, long length) {
574705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen        enforceMainLooper();
575705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen        mTimerModel.updateTimer(timer.setLength(length));
576705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen    }
577705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen
578705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen    /**
57958d9315aed4f645eb60c22be117b074e18c0982fJames Lemieux     * @param timer the timer whose {@code remainingTime} to change
580705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen     * @param remainingTime the new remaining time of the timer in milliseconds
581705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen     */
582705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen    public void setRemainingTime(Timer timer, long remainingTime) {
583705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen        enforceMainLooper();
584705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen
585705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen        final Timer updated = timer.setRemainingTime(remainingTime);
586705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen        mTimerModel.updateTimer(updated);
587705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen        if (timer.isRunning() && timer.getRemainingTime() <= 0) {
588705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen            mContext.startService(TimerService.createTimerExpiredIntent(mContext, updated));
589705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen        }
590705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen    }
591705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen
592705928dc4b0ed61e28d7f2807d5af3aaa9075806Justin Klaassen    /**
5936d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * Updates the timer notifications to be current.
5946d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
5956d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void updateTimerNotification() {
5966d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
5976d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.updateNotification();
5986d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
5996d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
6006d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
601bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux     * @return the uri of the default ringtone to play for all timers when no user selection exists
602bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux     */
603bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux    public Uri getDefaultTimerRingtoneUri() {
604bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux        enforceMainLooper();
605bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux        return mTimerModel.getDefaultTimerRingtoneUri();
606bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux    }
607bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux
608bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux    /**
609f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux     * @return {@code true} iff the ringtone to play for all timers is the silent ringtone
610f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux     */
611f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux    public boolean isTimerRingtoneSilent() {
612f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux        enforceMainLooper();
613f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux        return mTimerModel.isTimerRingtoneSilent();
614f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux    }
615f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux
616f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux    /**
617856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux     * @return the uri of the ringtone to play for all timers
618856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux     */
619856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    public Uri getTimerRingtoneUri() {
620856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux        enforceMainLooper();
621856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux        return mTimerModel.getTimerRingtoneUri();
622856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    }
623856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux
624856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    /**
6253101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan     * @param uri the uri of the ringtone to play for all timers
6263101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan     */
6273101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan    public void setTimerRingtoneUri(Uri uri) {
6283101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan        enforceMainLooper();
6293101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan        mTimerModel.setTimerRingtoneUri(uri);
6303101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan    }
6313101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan
6323101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan    /**
633856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux     * @return the title of the ringtone that is played for all timers
634856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux     */
635856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    public String getTimerRingtoneTitle() {
636856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux        enforceMainLooper();
637856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux        return mTimerModel.getTimerRingtoneTitle();
638856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    }
639856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux
64009c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin    /**
6410777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     * @return the duration, in milliseconds, of the crescendo to apply to timer ringtone playback;
6420777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     *      {@code 0} implies no crescendo should be applied
6430777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     */
6440777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    public long getTimerCrescendoDuration() {
6450777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux        enforceMainLooper();
6460777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux        return mTimerModel.getTimerCrescendoDuration();
6470777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    }
6480777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux
6490777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    /**
65009c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin     * @return whether vibrate is enabled for all timers.
65109c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin     */
65209c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin    public boolean getTimerVibrate() {
65309c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin        enforceMainLooper();
65409c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin        return mTimerModel.getTimerVibrate();
65509c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin    }
65609c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin
65709c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin    /**
65809c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin     * @param enabled whether vibrate is enabled for all timers.
65909c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin     */
66009c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin    public void setTimerVibrate(boolean enabled) {
66109c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin        enforceMainLooper();
66209c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin        mTimerModel.setTimerVibrate(enabled);
66309c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin    }
66409c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin
665856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    //
6666a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    // Alarms
6676a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    //
6686a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux
6696a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    /**
6706a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux     * @return the uri of the ringtone to which all new alarms default
6716a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux     */
6726a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    public Uri getDefaultAlarmRingtoneUri() {
6736a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux        enforceMainLooper();
6746a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux        return mAlarmModel.getDefaultAlarmRingtoneUri();
6756a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    }
6766a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux
6776a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    /**
6786a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux     * @param uri the uri of the ringtone to which future new alarms will default
6796a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux     */
6806a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    public void setDefaultAlarmRingtoneUri(Uri uri) {
6816a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux        enforceMainLooper();
6826a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux        mAlarmModel.setDefaultAlarmRingtoneUri(uri);
6836a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    }
6846a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux
6850777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    /**
6860777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     * @return the duration, in milliseconds, of the crescendo to apply to alarm ringtone playback;
6870777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     *      {@code 0} implies no crescendo should be applied
6880777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     */
6890777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    public long getAlarmCrescendoDuration() {
6900777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux        enforceMainLooper();
6910777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux        return mAlarmModel.getAlarmCrescendoDuration();
6920777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    }
6930777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux
6940777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    /**
6950777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     * @return the behavior to execute when volume buttons are pressed while firing an alarm
6960777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     */
6970777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    public AlarmVolumeButtonBehavior getAlarmVolumeButtonBehavior() {
6980777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux        enforceMainLooper();
6990777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux        return mAlarmModel.getAlarmVolumeButtonBehavior();
7000777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    }
7010777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux
7020777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    /**
7030777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     * @return the number of minutes an alarm may ring before it has timed out and becomes missed
7040777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     */
7050777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    public int getAlarmTimeout() {
7060777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux        return mAlarmModel.getAlarmTimeout();
7070777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    }
7080777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux
7090777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    /**
7100777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     * @return the number of minutes an alarm will remain snoozed before it rings again
7110777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     */
7120777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    public int getSnoozeLength() {
7130777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux        return mAlarmModel.getSnoozeLength();
7140777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    }
7150777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux
7166a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    //
71724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    // Stopwatch
71824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    //
71924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
72024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
7219042b0b548db623f7f465767008a27d305299aa9James Lemieux     * @param stopwatchListener to be notified when stopwatch changes or laps are added
7229042b0b548db623f7f465767008a27d305299aa9James Lemieux     */
7239042b0b548db623f7f465767008a27d305299aa9James Lemieux    public void addStopwatchListener(StopwatchListener stopwatchListener) {
7249042b0b548db623f7f465767008a27d305299aa9James Lemieux        enforceMainLooper();
7259042b0b548db623f7f465767008a27d305299aa9James Lemieux        mStopwatchModel.addStopwatchListener(stopwatchListener);
7269042b0b548db623f7f465767008a27d305299aa9James Lemieux    }
7279042b0b548db623f7f465767008a27d305299aa9James Lemieux
7289042b0b548db623f7f465767008a27d305299aa9James Lemieux    /**
7299042b0b548db623f7f465767008a27d305299aa9James Lemieux     * @param stopwatchListener to no longer be notified when stopwatch changes or laps are added
7309042b0b548db623f7f465767008a27d305299aa9James Lemieux     */
7319042b0b548db623f7f465767008a27d305299aa9James Lemieux    public void removeStopwatchListener(StopwatchListener stopwatchListener) {
7329042b0b548db623f7f465767008a27d305299aa9James Lemieux        enforceMainLooper();
7339042b0b548db623f7f465767008a27d305299aa9James Lemieux        mStopwatchModel.removeStopwatchListener(stopwatchListener);
7349042b0b548db623f7f465767008a27d305299aa9James Lemieux    }
7359042b0b548db623f7f465767008a27d305299aa9James Lemieux
7369042b0b548db623f7f465767008a27d305299aa9James Lemieux    /**
73724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the current state of the stopwatch
73824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
73924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public Stopwatch getStopwatch() {
74024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
74124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.getStopwatch();
74224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
74324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
74424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
74524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the stopwatch after being started
74624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
74724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public Stopwatch startStopwatch() {
74824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
74924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.setStopwatch(getStopwatch().start());
75024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
75124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
75224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
75324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the stopwatch after being paused
75424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
75524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public Stopwatch pauseStopwatch() {
75624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
75724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.setStopwatch(getStopwatch().pause());
75824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
75924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
76024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
76124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the stopwatch after being reset
76224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
76324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public Stopwatch resetStopwatch() {
76424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
76524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.setStopwatch(getStopwatch().reset());
76624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
76724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
76824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
76924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the laps recorded for this stopwatch
77024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
77124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public List<Lap> getLaps() {
77224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
77324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.getLaps();
77424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
77524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
77624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
77724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return a newly recorded lap completed now; {@code null} if no more laps can be added
77824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
77924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public Lap addLap() {
78024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
78124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.addLap();
78224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
78324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
78424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
78524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return {@code true} iff more laps can be recorded
78624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
78724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public boolean canAddMoreLaps() {
78824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
78924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.canAddMoreLaps();
79024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
79124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
79224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
79324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the longest lap time of all recorded laps and the current lap
79424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
79524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public long getLongestLapTime() {
79624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
79724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.getLongestLapTime();
79824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
79924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
80024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
80124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @param time a point in time after the end of the last lap
80224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the elapsed time between the given {@code time} and the end of the previous lap
80324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
80424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public long getCurrentLapTime(long time) {
80524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
80624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.getCurrentLapTime(time);
80724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
80824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
80924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    //
810be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout    // Time
8110b19985ecfa0f43acb8ededc173f643843098a95James Lemieux    // (Time settings/values are accessible from any Thread so no Thread-enforcement exists.)
812be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout    //
813be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout
814be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout    /**
8150b19985ecfa0f43acb8ededc173f643843098a95James Lemieux     * @return the current time in milliseconds
816be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout     */
817be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout    public long currentTimeMillis() {
818be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout        return mTimeModel.currentTimeMillis();
819be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout    }
820be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout
821be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout    /**
8220b19985ecfa0f43acb8ededc173f643843098a95James Lemieux     * @return milliseconds since boot, including time spent in sleep
823be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout     */
824be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout    public long elapsedRealtime() {
825be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout        return mTimeModel.elapsedRealtime();
826be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout    }
827be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout
8280b19985ecfa0f43acb8ededc173f643843098a95James Lemieux    /**
8290b19985ecfa0f43acb8ededc173f643843098a95James Lemieux     * @return {@code true} if 24 hour time format is selected; {@code false} otherwise
8300b19985ecfa0f43acb8ededc173f643843098a95James Lemieux     */
8310b19985ecfa0f43acb8ededc173f643843098a95James Lemieux    public boolean is24HourFormat() {
8320b19985ecfa0f43acb8ededc173f643843098a95James Lemieux        return mTimeModel.is24HourFormat();
8330b19985ecfa0f43acb8ededc173f643843098a95James Lemieux    }
8340b19985ecfa0f43acb8ededc173f643843098a95James Lemieux
8350b19985ecfa0f43acb8ededc173f643843098a95James Lemieux    /**
8360b19985ecfa0f43acb8ededc173f643843098a95James Lemieux     * @return a new calendar object initialized to the {@link #currentTimeMillis()}
8370b19985ecfa0f43acb8ededc173f643843098a95James Lemieux     */
8380b19985ecfa0f43acb8ededc173f643843098a95James Lemieux    public Calendar getCalendar() {
8390b19985ecfa0f43acb8ededc173f643843098a95James Lemieux        return mTimeModel.getCalendar();
8400b19985ecfa0f43acb8ededc173f643843098a95James Lemieux    }
8410b19985ecfa0f43acb8ededc173f643843098a95James Lemieux
842be74d5fefb57de5ec6bb986bb3ef6f2874da5a00Sean Stout    //
84333781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    // Ringtones
84433781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    //
84533781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux
84633781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    /**
847c9446b52d9391e859e65d11e5342d84cfe3e781cJames Lemieux     * Ringtone titles are cached because loading them is expensive. This method
848c9446b52d9391e859e65d11e5342d84cfe3e781cJames Lemieux     * <strong>must</strong> be called on a background thread and is responsible for priming the
849c9446b52d9391e859e65d11e5342d84cfe3e781cJames Lemieux     * cache of ringtone titles to avoid later fetching titles on the main thread.
850c9446b52d9391e859e65d11e5342d84cfe3e781cJames Lemieux     */
851c9446b52d9391e859e65d11e5342d84cfe3e781cJames Lemieux    public void loadRingtoneTitles() {
852c9446b52d9391e859e65d11e5342d84cfe3e781cJames Lemieux        enforceNotMainLooper();
853c9446b52d9391e859e65d11e5342d84cfe3e781cJames Lemieux        mRingtoneModel.loadRingtoneTitles();
854c9446b52d9391e859e65d11e5342d84cfe3e781cJames Lemieux    }
855c9446b52d9391e859e65d11e5342d84cfe3e781cJames Lemieux
856c9446b52d9391e859e65d11e5342d84cfe3e781cJames Lemieux    /**
85733781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux     * @param uri the uri of a ringtone
85833781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux     * @return the title of the ringtone with the {@code uri}; {@code null} if it cannot be fetched
85933781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux     */
86033781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    public String getRingtoneTitle(Uri uri) {
86133781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux        enforceMainLooper();
86233781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux        return mRingtoneModel.getRingtoneTitle(uri);
86333781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    }
86433781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux
86533781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    /**
86633781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux     * @param uri the uri of an audio file to use as a ringtone
86733781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux     * @param title the title of the audio content at the given {@code uri}
86833781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux     * @return the ringtone instance created for the audio file
86933781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux     */
87033781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    public CustomRingtone addCustomRingtone(Uri uri, String title) {
87133781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux        enforceMainLooper();
87233781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux        return mRingtoneModel.addCustomRingtone(uri, title);
87333781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    }
87433781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux
87533781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    /**
87633781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux     * @param uri identifies the ringtone to remove
87733781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux     */
87833781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    public void removeCustomRingtone(Uri uri) {
87933781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux        enforceMainLooper();
88033781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux        mRingtoneModel.removeCustomRingtone(uri);
88133781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    }
88233781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux
88333781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    /**
88433781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux     * @return all available custom ringtones
88533781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux     */
88633781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    public List<CustomRingtone> getCustomRingtones() {
88733781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux        enforceMainLooper();
88833781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux        return mRingtoneModel.getCustomRingtones();
88933781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    }
89033781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux
89133781c882e4229f4ec1a8fafbabb9d4b8b8e2932James Lemieux    //
892592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    // Widgets
893592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    //
894592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux
895592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    /**
896592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux     * @param widgetClass indicates the type of widget being counted
897592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux     * @param count the number of widgets of the given type
898592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux     * @param eventCategoryId identifies the category of event to send
899592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux     */
900592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    public void updateWidgetCount(Class widgetClass, int count, @StringRes int eventCategoryId) {
901592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux        enforceMainLooper();
902592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux        mWidgetModel.updateWidgetCount(widgetClass, count, eventCategoryId);
903592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    }
904592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux
905592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    //
90634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    // Settings
90734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    //
90834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
90934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
91025384711e3c2894a84642c49953594930dd078abJames Lemieux     * @param silentSettingsListener to be notified when alarm-silencing settings change
91125384711e3c2894a84642c49953594930dd078abJames Lemieux     */
91225384711e3c2894a84642c49953594930dd078abJames Lemieux    public void addSilentSettingsListener(OnSilentSettingsListener silentSettingsListener) {
91325384711e3c2894a84642c49953594930dd078abJames Lemieux        enforceMainLooper();
91425384711e3c2894a84642c49953594930dd078abJames Lemieux        mSilentSettingsModel.addSilentSettingsListener(silentSettingsListener);
91525384711e3c2894a84642c49953594930dd078abJames Lemieux    }
91625384711e3c2894a84642c49953594930dd078abJames Lemieux
91725384711e3c2894a84642c49953594930dd078abJames Lemieux    /**
91825384711e3c2894a84642c49953594930dd078abJames Lemieux     * @param silentSettingsListener to no longer be notified when alarm-silencing settings change
91925384711e3c2894a84642c49953594930dd078abJames Lemieux     */
92025384711e3c2894a84642c49953594930dd078abJames Lemieux    public void removeSilentSettingsListener(OnSilentSettingsListener silentSettingsListener) {
92125384711e3c2894a84642c49953594930dd078abJames Lemieux        enforceMainLooper();
92225384711e3c2894a84642c49953594930dd078abJames Lemieux        mSilentSettingsModel.removeSilentSettingsListener(silentSettingsListener);
92325384711e3c2894a84642c49953594930dd078abJames Lemieux    }
92425384711e3c2894a84642c49953594930dd078abJames Lemieux
92525384711e3c2894a84642c49953594930dd078abJames Lemieux    /**
9260777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     * @return the id used to discriminate relevant AlarmManager callbacks from defunct ones
9270777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     */
9280777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    public int getGlobalIntentId() {
9290777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux        return mSettingsModel.getGlobalIntentId();
9300777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    }
9310777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux
9320777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    /**
9330777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     * Update the id used to discriminate relevant AlarmManager callbacks from defunct ones
9340777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     */
9350777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    public void updateGlobalIntentId() {
9360777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux        enforceMainLooper();
9370777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux        mSettingsModel.updateGlobalIntentId();
9380777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    }
9390777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux
9400777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    /**
94134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return the style of clock to display in the clock application
94234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
94334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public ClockStyle getClockStyle() {
94434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
94534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mSettingsModel.getClockStyle();
94634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
94734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
94834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
9497b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks     * @return the style of clock to display in the clock application
9507b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks     */
9517b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks    public boolean getDisplayClockSeconds() {
9527b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks        enforceMainLooper();
9537b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks        return mSettingsModel.getDisplayClockSeconds();
9547b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks    }
9557b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks
9567b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks    /**
9577b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks     * @param displaySeconds whether or not to display seconds for main clock
9587b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks     */
9597b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks    public void setDisplayClockSeconds(boolean displaySeconds) {
9607b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks        enforceMainLooper();
9617b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks        mSettingsModel.setDisplayClockSeconds(displaySeconds);
9627b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks    }
9637b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks
9647b852b43db6d9e50814dd07366b025b3f9f9b758Christine Franks    /**
96534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return the style of clock to display in the clock screensaver
96634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
96734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public ClockStyle getScreensaverClockStyle() {
96834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
96934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mSettingsModel.getScreensaverClockStyle();
97034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
97134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
97234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
9733af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux     * @return {@code true} if the screen saver should be dimmed for lower contrast at night
9743af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux     */
9753af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux    public boolean getScreensaverNightModeOn() {
9763af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux        enforceMainLooper();
9773af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux        return mSettingsModel.getScreensaverNightModeOn();
9783af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux    }
9793af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux
9803af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux    /**
98134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return {@code true} if the users wants to automatically show a clock for their home timezone
98234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     *      when they have travelled outside of that timezone
98334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
98434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public boolean getShowHomeClock() {
98534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
98634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mSettingsModel.getShowHomeClock();
98734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
988d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
989d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    /**
990ff17acface9b98eba868fff0e2d70ddc85c5e4dbJames Lemieux     * @return the display order of the weekdays, which can start with {@link Calendar#SATURDAY},
991ff17acface9b98eba868fff0e2d70ddc85c5e4dbJames Lemieux     *      {@link Calendar#SUNDAY} or {@link Calendar#MONDAY}
992458aa8b4ebb8b7c6fdc0680a1b687ea21a61bf35James Lemieux     */
993ff17acface9b98eba868fff0e2d70ddc85c5e4dbJames Lemieux    public Weekdays.Order getWeekdayOrder() {
994458aa8b4ebb8b7c6fdc0680a1b687ea21a61bf35James Lemieux        enforceMainLooper();
995ff17acface9b98eba868fff0e2d70ddc85c5e4dbJames Lemieux        return mSettingsModel.getWeekdayOrder();
996458aa8b4ebb8b7c6fdc0680a1b687ea21a61bf35James Lemieux    }
997458aa8b4ebb8b7c6fdc0680a1b687ea21a61bf35James Lemieux
998458aa8b4ebb8b7c6fdc0680a1b687ea21a61bf35James Lemieux    /**
9990777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     * @return {@code true} if the restore process (of backup and restore) has completed
10000777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     */
10010777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    public boolean isRestoreBackupFinished() {
10020777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux        return mSettingsModel.isRestoreBackupFinished();
10030777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    }
10040777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux
10050777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    /**
10060777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     * @param finished {@code true} means the restore process (of backup and restore) has completed
10070777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux     */
10080777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    public void setRestoreBackupFinished(boolean finished) {
10090777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux        mSettingsModel.setRestoreBackupFinished(finished);
10100777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    }
10110777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux
10120777a082e248c33c2e5a32690bfcdbd5de7d3f9eJames Lemieux    /**
1013d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     * Used to execute a delegate runnable and track its completion.
1014d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     */
1015d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    private static class ExecutedRunnable implements Runnable {
1016d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
1017d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        private final Runnable mDelegate;
1018d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        private boolean mExecuted;
1019d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
1020d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        private ExecutedRunnable(Runnable delegate) {
1021d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            this.mDelegate = delegate;
1022d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        }
1023d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
1024d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        @Override
1025d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        public void run() {
1026d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            mDelegate.run();
1027d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
1028d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            synchronized (this) {
1029d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan                mExecuted = true;
1030d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan                notifyAll();
1031d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            }
1032d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        }
1033d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
1034d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        private boolean isExecuted() {
1035d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            return mExecuted;
1036d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        }
1037d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    }
1038ae9a055482865a70028fc9940c5d871775f9620cJustin Klaassen}
1039