DataModel.java revision ff17acface9b98eba868fff0e2d70ddc85c5e4db
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;
21856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieuximport android.net.Uri;
22d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phanimport android.os.Handler;
23d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phanimport android.os.Looper;
246d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.support.annotation.StringRes;
2534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
260a3313e231702cc9944e9a17e52aea62eb25afabSean Stoutimport com.android.deskclock.timer.TimerService;
270a3313e231702cc9944e9a17e52aea62eb25afabSean Stout
28458aa8b4ebb8b7c6fdc0680a1b687ea21a61bf35James Lemieuximport java.util.Calendar;
2934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieuximport java.util.Collection;
3034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieuximport java.util.Comparator;
3134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieuximport java.util.List;
3234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
3334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieuximport static com.android.deskclock.Utils.enforceMainLooper;
3434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
3534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux/**
3634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux * All application-wide data is accessible through this singleton.
3734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux */
3834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieuxpublic final class DataModel {
3934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
4034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /** Indicates the display style of clocks. */
4134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public enum ClockStyle {ANALOG, DIGITAL}
4234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
4334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /** Indicates the preferred sort order of cities. */
4434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public enum CitySort {NAME, UTC_OFFSET}
4534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
468bf1c3ba34724773b92242effec8330e1852bb7bJames Lemieux    public static final String ACTION_WORLD_CITIES_CHANGED =
478bf1c3ba34724773b92242effec8330e1852bb7bJames Lemieux            "com.android.deskclock.WORLD_CITIES_CHANGED";
488bf1c3ba34724773b92242effec8330e1852bb7bJames Lemieux
4934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /** The single instance of this data model that exists for the life of the application. */
5034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    private static final DataModel sDataModel = new DataModel();
5134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
52d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    private Handler mHandler;
53d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
5434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    private Context mContext;
5534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
5634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /** The model from which settings are fetched. */
5734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    private SettingsModel mSettingsModel;
5834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
59856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    /** The model from which city data are fetched. */
6034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    private CityModel mCityModel;
6134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
62856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    /** The model from which timer data are fetched. */
63856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    private TimerModel mTimerModel;
64856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux
656a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    /** The model from which alarm data are fetched. */
666a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    private AlarmModel mAlarmModel;
676a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux
68592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    /** The model from which widget data are fetched. */
69592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    private WidgetModel mWidgetModel;
70592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux
7124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /** The model from which stopwatch data are fetched. */
7224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    private StopwatchModel mStopwatchModel;
7324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
7424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /** The model from which notification data are fetched. */
7524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    private NotificationModel mNotificationModel;
7624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
7734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public static DataModel getDataModel() {
7834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return sDataModel;
7934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
8034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
8134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    private DataModel() {}
8234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
8334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
8434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * The context may be set precisely once during the application life.
8534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
8634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public void setContext(Context context) {
8734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        if (mContext != null) {
8834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux            throw new IllegalStateException("context has already been set");
8934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        }
9034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        mContext = context.getApplicationContext();
9124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
9234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        mSettingsModel = new SettingsModel(mContext);
9324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        mNotificationModel = new NotificationModel();
9434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        mCityModel = new CityModel(mContext, mSettingsModel);
95432b07cdbafb9733fea76e9511b6b5f6c15aa8c8James Lemieux        mWidgetModel = new WidgetModel(mContext);
966a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux        mAlarmModel = new AlarmModel(mContext, mSettingsModel);
9724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        mStopwatchModel = new StopwatchModel(mContext, mNotificationModel);
986d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel = new TimerModel(mContext, mSettingsModel, mNotificationModel);
9924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
10024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
101d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    /**
102a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen     * Convenience for {@code run(runnable, 0)}, i.e. waits indefinitely.
103a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen     */
104a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen    public void run(Runnable runnable) {
105a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen        try {
106a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen            run(runnable, 0 /* waitMillis */);
107a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen        } catch (InterruptedException ignored) {
108a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen        }
109a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen    }
110a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen
111a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen    /**
1122a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     * Updates all timers and the stopwatch after the device has shutdown and restarted.
1132a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     */
1142a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    public void updateAfterReboot() {
1152a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        enforceMainLooper();
1162a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mTimerModel.updateTimersAfterReboot();
1172a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mStopwatchModel.setStopwatch(getStopwatch().updateAfterReboot());
1182a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    }
1192a07ae3286fd5c76f71546890e0f02af99065825Sean Stout
1202a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    /**
1212a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     * Updates all timers and the stopwatch after the device's time has changed.
1222a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     */
1232a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    public void updateAfterTimeSet() {
1242a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        enforceMainLooper();
1252a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mTimerModel.updateTimersAfterTimeSet();
1262a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mStopwatchModel.setStopwatch(getStopwatch().updateAfterTimeSet());
1272a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    }
1282a07ae3286fd5c76f71546890e0f02af99065825Sean Stout
1292a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    /**
130d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     * Posts a runnable to the main thread and blocks until the runnable executes. Used to access
131d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     * the data model from the main thread.
132d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     */
133a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen    public void run(Runnable runnable, long waitMillis) throws InterruptedException {
134d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        if (Looper.myLooper() == Looper.getMainLooper()) {
135d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            runnable.run();
136d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            return;
137d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        }
138d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
139d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        final ExecutedRunnable er = new ExecutedRunnable(runnable);
140d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        getHandler().post(er);
141d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
142d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        // Wait for the data to arrive, if it has not.
143d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        synchronized (er) {
144d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            if (!er.isExecuted()) {
145a5c107781dc8f8239bc980d19e1e31ed262e8829Justin Klaassen                er.wait(waitMillis);
146d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            }
147d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        }
148d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    }
149d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
150d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    /**
151d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     * @return a handler associated with the main thread
152d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     */
153d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    private synchronized Handler getHandler() {
154d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        if (mHandler == null) {
155d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            mHandler = new Handler(Looper.getMainLooper());
156d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        }
157d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        return mHandler;
158d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    }
159d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
16024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    //
16124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    // Application
16224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    //
16324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
16424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
16524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @param inForeground {@code true} to indicate the application is open in the foreground
16624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
16724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public void setApplicationInForeground(boolean inForeground) {
16824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
16924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
17024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        if (mNotificationModel.isApplicationInForeground() != inForeground) {
17124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux            mNotificationModel.setApplicationInForeground(inForeground);
17224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
17324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux            // Refresh all notifications in response to a change in app open state.
1746d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            mTimerModel.updateNotification();
1752a07ae3286fd5c76f71546890e0f02af99065825Sean Stout            mTimerModel.updateMissedNotification();
17624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux            mStopwatchModel.updateNotification();
17724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        }
17824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
17924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
18024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
18124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return {@code true} when the application is open in the foreground; {@code false} otherwise
18224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
18324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public boolean isApplicationInForeground() {
1843101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan        enforceMainLooper();
18524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mNotificationModel.isApplicationInForeground();
18634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
18734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
188ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux    /**
189ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux     * Called when the notifications may be stale or absent from the notification manager and must
190ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux     * be rebuilt. e.g. after upgrading the application
191ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux     */
192ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux    public void updateAllNotifications() {
193376b0f51995803a93d7a6907f8ed30aff06e2aaeJames Lemieux        enforceMainLooper();
194ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux        mTimerModel.updateNotification();
1952a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mTimerModel.updateMissedNotification();
196ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux        mStopwatchModel.updateNotification();
197ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux    }
198ac1506f175631e2b2f52ff9142cbdc6c725ef447James Lemieux
19934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    //
20034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    // Cities
20134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    //
20234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
20334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
20434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return a list of all cities in their display order
20534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
20634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public List<City> getAllCities() {
20734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
20834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mCityModel.getAllCities();
20934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
21034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
21134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
21234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @param cityName the case-insensitive city name to search for
21334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return the city with the given {@code cityName}; {@code null} if no such city exists
21434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
21534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public City getCity(String cityName) {
21634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
21734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mCityModel.getCity(cityName);
21834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
21934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
22034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
22134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return a city representing the user's home timezone
22234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
22334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public City getHomeCity() {
22434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
22534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mCityModel.getHomeCity();
22634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
22734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
22834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
22934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return a list of cities not selected for display
23034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
23134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public List<City> getUnselectedCities() {
23234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
23334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mCityModel.getUnselectedCities();
23434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
23534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
23634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
23734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return a list of cities selected for display
23834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
23934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public List<City> getSelectedCities() {
24034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
24134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mCityModel.getSelectedCities();
24234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
24334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
24434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
24534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @param cities the new collection of cities selected for display by the user
24634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
24734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public void setSelectedCities(Collection<City> cities) {
24834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
24934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        mCityModel.setSelectedCities(cities);
25034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
25134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
25234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
25334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return a comparator used to locate index positions
25434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
25534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public Comparator<City> getCityIndexComparator() {
25634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
25734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mCityModel.getCityIndexComparator();
25834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
25934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
26034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
26134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return the order in which cities are sorted
26234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
26334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public CitySort getCitySort() {
26434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
26534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mCityModel.getCitySort();
26634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
26734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
26834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
26934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * Adjust the order in which cities are sorted.
27034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
27134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public void toggleCitySort() {
27234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
27334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        mCityModel.toggleCitySort();
27434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
27534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
276a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout    /**
277a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout     * @param cityListener listener to be notified when the world city list changes
278a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout     */
279a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout    public void addCityListener(CityListener cityListener) {
280a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout        enforceMainLooper();
281a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout        mCityModel.addCityListener(cityListener);
282a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout    }
283a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout
284a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout    /**
285a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout     * @param cityListener listener that no longer needs to be notified of world city list changes
286a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout     */
287a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout    public void removeCityListener(CityListener cityListener) {
288a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout        enforceMainLooper();
289a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout        mCityModel.removeCityListener(cityListener);
290a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout    }
291a10158df7b7e056ec5c63df9cfaacd88d0e79c7fSean Stout
29234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    //
293856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    // Timers
294856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    //
295856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux
296856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    /**
2976d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timerListener to be notified when timers are added, updated and removed
2986d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
2996d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void addTimerListener(TimerListener timerListener) {
3006d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
3016d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.addTimerListener(timerListener);
3026d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
3036d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
3046d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
3056d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timerListener to no longer be notified when timers are added, updated and removed
3066d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
3076d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void removeTimerListener(TimerListener timerListener) {
3086d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
3096d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.removeTimerListener(timerListener);
3106d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
3116d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
3126d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
3136d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @return a list of timers for display
3146d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
3156d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public List<Timer> getTimers() {
3166d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
3176d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return mTimerModel.getTimers();
3186d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
3196d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
3206d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
3216d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @return a list of expired timers for display
3226d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
3236d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public List<Timer> getExpiredTimers() {
3246d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
3256d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return mTimerModel.getExpiredTimers();
3266d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
3276d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
3286d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
3296d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timerId identifies the timer to return
3306d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @return the timer with the given {@code timerId}
3316d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
3326d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public Timer getTimer(int timerId) {
3336d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
3346d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return mTimerModel.getTimer(timerId);
3356d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
3366d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
3376d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
3386d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @return the timer that last expired and is still expired now; {@code null} if no timers are
3396d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     *      expired
3406d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
3416d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public Timer getMostRecentExpiredTimer() {
3426d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
3436d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return mTimerModel.getMostRecentExpiredTimer();
3446d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
3456d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
3466d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
3476d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param length the length of the timer in milliseconds
3486d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param label describes the purpose of the timer
349437da3b08ce9ce1b32f4e544816cb3431ceb8d4eJames Lemieux     * @param deleteAfterUse {@code true} indicates the timer should be deleted when it is reset
3506d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @return the newly added timer
3516d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
352437da3b08ce9ce1b32f4e544816cb3431ceb8d4eJames Lemieux    public Timer addTimer(long length, String label, boolean deleteAfterUse) {
3536d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
354437da3b08ce9ce1b32f4e544816cb3431ceb8d4eJames Lemieux        return mTimerModel.addTimer(length, label, deleteAfterUse);
3556d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
3566d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
3576d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
3586d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to be removed
3596d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
3606d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void removeTimer(Timer timer) {
3616d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
3626d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.removeTimer(timer);
3636d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
3646d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
3656d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
3666d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to be started
3676d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
3686d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void startTimer(Timer timer) {
3690a3313e231702cc9944e9a17e52aea62eb25afabSean Stout        startTimer(null, timer);
3700a3313e231702cc9944e9a17e52aea62eb25afabSean Stout    }
3710a3313e231702cc9944e9a17e52aea62eb25afabSean Stout
3720a3313e231702cc9944e9a17e52aea62eb25afabSean Stout    /**
3730a3313e231702cc9944e9a17e52aea62eb25afabSean Stout     * @param service used to start foreground notifications for expired timers
3740a3313e231702cc9944e9a17e52aea62eb25afabSean Stout     * @param timer the timer to be started
3750a3313e231702cc9944e9a17e52aea62eb25afabSean Stout     */
3760a3313e231702cc9944e9a17e52aea62eb25afabSean Stout    public void startTimer(Service service, Timer timer) {
3776d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
3780a3313e231702cc9944e9a17e52aea62eb25afabSean Stout        final Timer started = timer.start();
3790a3313e231702cc9944e9a17e52aea62eb25afabSean Stout        mTimerModel.updateTimer(started);
3800a3313e231702cc9944e9a17e52aea62eb25afabSean Stout        if (timer.getRemainingTime() <= 0) {
3810a3313e231702cc9944e9a17e52aea62eb25afabSean Stout            if (service != null) {
3820a3313e231702cc9944e9a17e52aea62eb25afabSean Stout                expireTimer(service, started);
3830a3313e231702cc9944e9a17e52aea62eb25afabSean Stout            } else {
3840a3313e231702cc9944e9a17e52aea62eb25afabSean Stout                mContext.startService(TimerService.createTimerExpiredIntent(mContext, started));
3850a3313e231702cc9944e9a17e52aea62eb25afabSean Stout            }
3860a3313e231702cc9944e9a17e52aea62eb25afabSean Stout        }
3876d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
3886d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
3896d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
3906d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to be paused
3916d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
3926d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void pauseTimer(Timer timer) {
3936d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
3946d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.updateTimer(timer.pause());
3956d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
3966d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
3976d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
3980dd0cac610cd59762c8b604da6c437b18a29246bJames Lemieux     * @param service used to start foreground notifications for expired timers
3996d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to be expired
4006d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4010dd0cac610cd59762c8b604da6c437b18a29246bJames Lemieux    public void expireTimer(Service service, Timer timer) {
4026d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4030dd0cac610cd59762c8b604da6c437b18a29246bJames Lemieux        mTimerModel.expireTimer(service, timer);
4046d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4056d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4066d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4076d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * If the given {@code timer} is expired and marked for deletion after use then this method
4086d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * removes the the timer. The timer is otherwise transitioned to the reset state and continues
4096d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * to exist.
4106d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     *
4116d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to be reset
4126d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
413b10e1abe917de457e9cb3be12ffd5d2634d8f79cJames Lemieux     * @return the reset {@code timer} or {@code null} if the timer was deleted
4146d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
415b10e1abe917de457e9cb3be12ffd5d2634d8f79cJames Lemieux    public Timer resetOrDeleteTimer(Timer timer, @StringRes int eventLabelId) {
4166d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
417b10e1abe917de457e9cb3be12ffd5d2634d8f79cJames Lemieux        return mTimerModel.resetOrDeleteTimer(timer, eventLabelId);
4186d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4196d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4206d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4212a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     * Resets all expired timers.
4226d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     *
4236d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
4246d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4252a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    public void resetExpiredTimers(@StringRes int eventLabelId) {
4266d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4272a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mTimerModel.resetExpiredTimers(eventLabelId);
4286d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4296d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4306d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4312a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     * Resets all unexpired timers.
4326d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     *
4336d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
4346d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4352a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    public void resetUnexpiredTimers(@StringRes int eventLabelId) {
4366d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4372a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mTimerModel.resetUnexpiredTimers(eventLabelId);
4386d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4396d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4406d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4412a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     * Resets all missed timers.
4426d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     *
4436d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
4446d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4452a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    public void resetMissedTimers(@StringRes int eventLabelId) {
4466d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4472a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mTimerModel.resetMissedTimers(eventLabelId);
4486d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4496d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4502a07ae3286fd5c76f71546890e0f02af99065825Sean Stout
4516d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4526d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to which a minute should be added to the remaining time
4536d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4546d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void addTimerMinute(Timer timer) {
4556d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4566d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.updateTimer(timer.addMinute());
4576d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4586d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4596d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4606d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param timer the timer to which the new {@code label} belongs
4616d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @param label the new label to store for the {@code timer}
4626d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4636d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void setTimerLabel(Timer timer, String label) {
4646d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4656d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.updateTimer(timer.setLabel(label));
4666d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4676d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4686d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4696d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * Updates the timer notifications to be current.
4706d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
4716d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void updateTimerNotification() {
4726d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        enforceMainLooper();
4736d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mTimerModel.updateNotification();
4746d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
4756d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
4766d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
4772a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     * Updates the missed timer notifications to be current.
4782a07ae3286fd5c76f71546890e0f02af99065825Sean Stout     */
4792a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    public void updateMissedTimerNotification() {
4802a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        enforceMainLooper();
4812a07ae3286fd5c76f71546890e0f02af99065825Sean Stout        mTimerModel.updateMissedNotification();
4822a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    }
4832a07ae3286fd5c76f71546890e0f02af99065825Sean Stout
4842a07ae3286fd5c76f71546890e0f02af99065825Sean Stout
4852a07ae3286fd5c76f71546890e0f02af99065825Sean Stout    /**
486bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux     * @return the uri of the default ringtone to play for all timers when no user selection exists
487bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux     */
488bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux    public Uri getDefaultTimerRingtoneUri() {
489bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux        enforceMainLooper();
490bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux        return mTimerModel.getDefaultTimerRingtoneUri();
491bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux    }
492bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux
493bd9eae10b13e015d1997d06f13e9abe06a7f306bJames Lemieux    /**
494f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux     * @return {@code true} iff the ringtone to play for all timers is the silent ringtone
495f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux     */
496f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux    public boolean isTimerRingtoneSilent() {
497f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux        enforceMainLooper();
498f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux        return mTimerModel.isTimerRingtoneSilent();
499f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux    }
500f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux
501f8faca1961278db2797d122351885ce6e32e4f3dJames Lemieux    /**
502856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux     * @return the uri of the ringtone to play for all timers
503856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux     */
504856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    public Uri getTimerRingtoneUri() {
505856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux        enforceMainLooper();
506856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux        return mTimerModel.getTimerRingtoneUri();
507856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    }
508856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux
509856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    /**
5103101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan     * @param uri the uri of the ringtone to play for all timers
5113101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan     */
5123101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan    public void setTimerRingtoneUri(Uri uri) {
5133101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan        enforceMainLooper();
5143101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan        mTimerModel.setTimerRingtoneUri(uri);
5153101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan    }
5163101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan
5173101fcf76ff5c228d2e643395be03d6cbf97d47eDylan Phan    /**
518856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux     * @return the title of the ringtone that is played for all timers
519856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux     */
520856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    public String getTimerRingtoneTitle() {
521856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux        enforceMainLooper();
522856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux        return mTimerModel.getTimerRingtoneTitle();
523856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    }
524856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux
52509c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin    /**
52609c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin     * @return whether vibrate is enabled for all timers.
52709c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin     */
52809c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin    public boolean getTimerVibrate() {
52909c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin        enforceMainLooper();
53009c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin        return mTimerModel.getTimerVibrate();
53109c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin    }
53209c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin
53309c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin    /**
53409c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin     * @param enabled whether vibrate is enabled for all timers.
53509c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin     */
53609c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin    public void setTimerVibrate(boolean enabled) {
53709c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin        enforceMainLooper();
53809c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin        mTimerModel.setTimerVibrate(enabled);
53909c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin    }
54009c1f0e96743fcde110ecd1160ab55cdfaec12c0Annie Chin
541856483e7e18d5f042a338f7b3d472e28a386c4adJames Lemieux    //
5426a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    // Alarms
5436a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    //
5446a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux
5456a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    /**
5466a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux     * @return the uri of the ringtone to which all new alarms default
5476a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux     */
5486a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    public Uri getDefaultAlarmRingtoneUri() {
5496a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux        enforceMainLooper();
5506a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux        return mAlarmModel.getDefaultAlarmRingtoneUri();
5516a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    }
5526a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux
5536a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    /**
5546a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux     * @param uri the uri of the ringtone to which future new alarms will default
5556a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux     */
5566a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    public void setDefaultAlarmRingtoneUri(Uri uri) {
5576a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux        enforceMainLooper();
5586a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux        mAlarmModel.setDefaultAlarmRingtoneUri(uri);
5596a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    }
5606a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux
5616a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    /**
5626a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux     * @param uri the uri of a ringtone
5636a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux     * @return the title of the ringtone with the {@code uri}; {@code null} if it cannot be fetched
5646a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux     */
5656a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    public String getAlarmRingtoneTitle(Uri uri) {
5666a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux        enforceMainLooper();
5676a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux        return mAlarmModel.getAlarmRingtoneTitle(uri);
5686a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    }
5696a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux
5706a59a7b2c034557bc8bc7481544db5cd1105a891James Lemieux    //
57124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    // Stopwatch
57224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    //
57324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
57424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
5759042b0b548db623f7f465767008a27d305299aa9James Lemieux     * @param stopwatchListener to be notified when stopwatch changes or laps are added
5769042b0b548db623f7f465767008a27d305299aa9James Lemieux     */
5779042b0b548db623f7f465767008a27d305299aa9James Lemieux    public void addStopwatchListener(StopwatchListener stopwatchListener) {
5789042b0b548db623f7f465767008a27d305299aa9James Lemieux        enforceMainLooper();
5799042b0b548db623f7f465767008a27d305299aa9James Lemieux        mStopwatchModel.addStopwatchListener(stopwatchListener);
5809042b0b548db623f7f465767008a27d305299aa9James Lemieux    }
5819042b0b548db623f7f465767008a27d305299aa9James Lemieux
5829042b0b548db623f7f465767008a27d305299aa9James Lemieux    /**
5839042b0b548db623f7f465767008a27d305299aa9James Lemieux     * @param stopwatchListener to no longer be notified when stopwatch changes or laps are added
5849042b0b548db623f7f465767008a27d305299aa9James Lemieux     */
5859042b0b548db623f7f465767008a27d305299aa9James Lemieux    public void removeStopwatchListener(StopwatchListener stopwatchListener) {
5869042b0b548db623f7f465767008a27d305299aa9James Lemieux        enforceMainLooper();
5879042b0b548db623f7f465767008a27d305299aa9James Lemieux        mStopwatchModel.removeStopwatchListener(stopwatchListener);
5889042b0b548db623f7f465767008a27d305299aa9James Lemieux    }
5899042b0b548db623f7f465767008a27d305299aa9James Lemieux
5909042b0b548db623f7f465767008a27d305299aa9James Lemieux    /**
59124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the current state of the stopwatch
59224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
59324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public Stopwatch getStopwatch() {
59424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
59524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.getStopwatch();
59624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
59724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
59824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
59924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the stopwatch after being started
60024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
60124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public Stopwatch startStopwatch() {
60224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
60324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.setStopwatch(getStopwatch().start());
60424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
60524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
60624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
60724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the stopwatch after being paused
60824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
60924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public Stopwatch pauseStopwatch() {
61024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
61124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.setStopwatch(getStopwatch().pause());
61224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
61324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
61424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
61524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the stopwatch after being reset
61624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
61724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public Stopwatch resetStopwatch() {
61824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
61924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.setStopwatch(getStopwatch().reset());
62024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
62124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
62224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
62324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the laps recorded for this stopwatch
62424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
62524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public List<Lap> getLaps() {
62624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
62724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.getLaps();
62824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
62924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
63024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
63124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return a newly recorded lap completed now; {@code null} if no more laps can be added
63224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
63324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public Lap addLap() {
63424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
63524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.addLap();
63624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
63724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
63824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
63924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return {@code true} iff more laps can be recorded
64024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
64124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public boolean canAddMoreLaps() {
64224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
64324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.canAddMoreLaps();
64424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
64524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
64624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
64724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the longest lap time of all recorded laps and the current lap
64824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
64924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public long getLongestLapTime() {
65024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
65124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.getLongestLapTime();
65224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
65324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
65424a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    /**
65524a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @param time a point in time after the end of the last lap
65624a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     * @return the elapsed time between the given {@code time} and the end of the previous lap
65724a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux     */
65824a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    public long getCurrentLapTime(long time) {
65924a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        enforceMainLooper();
66024a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux        return mStopwatchModel.getCurrentLapTime(time);
66124a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    }
66224a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux
66324a54fc16fdf95ee3f76ab99978c3401473dc516James Lemieux    //
664592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    // Widgets
665592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    //
666592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux
667592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    /**
668592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux     * @param widgetClass indicates the type of widget being counted
669592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux     * @param count the number of widgets of the given type
670592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux     * @param eventCategoryId identifies the category of event to send
671592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux     */
672592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    public void updateWidgetCount(Class widgetClass, int count, @StringRes int eventCategoryId) {
673592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux        enforceMainLooper();
674592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux        mWidgetModel.updateWidgetCount(widgetClass, count, eventCategoryId);
675592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    }
676592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux
677592c66b3eb497d24d1528fb2597059c6e4ec6620James Lemieux    //
67834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    // Settings
67934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    //
68034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
68134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
68234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return the style of clock to display in the clock application
68334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
68434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public ClockStyle getClockStyle() {
68534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
68634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mSettingsModel.getClockStyle();
68734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
68834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
68934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
69034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return the style of clock to display in the clock screensaver
69134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
69234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public ClockStyle getScreensaverClockStyle() {
69334142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
69434142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mSettingsModel.getScreensaverClockStyle();
69534142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
69634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux
69734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    /**
6983af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux     * @return {@code true} if the screen saver should be dimmed for lower contrast at night
6993af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux     */
7003af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux    public boolean getScreensaverNightModeOn() {
7013af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux        enforceMainLooper();
7023af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux        return mSettingsModel.getScreensaverNightModeOn();
7033af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux    }
7043af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux
7053af168c834d73487f8f614f0aaafbf6f9a850f0fJames Lemieux    /**
70634142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     * @return {@code true} if the users wants to automatically show a clock for their home timezone
70734142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     *      when they have travelled outside of that timezone
70834142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux     */
70934142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    public boolean getShowHomeClock() {
71034142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        enforceMainLooper();
71134142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux        return mSettingsModel.getShowHomeClock();
71234142b1d0f2445bbd606bb490dfef6c078c630eaJames Lemieux    }
713d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
714d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    /**
715ff17acface9b98eba868fff0e2d70ddc85c5e4dbJames Lemieux     * @return the display order of the weekdays, which can start with {@link Calendar#SATURDAY},
716ff17acface9b98eba868fff0e2d70ddc85c5e4dbJames Lemieux     *      {@link Calendar#SUNDAY} or {@link Calendar#MONDAY}
717458aa8b4ebb8b7c6fdc0680a1b687ea21a61bf35James Lemieux     */
718ff17acface9b98eba868fff0e2d70ddc85c5e4dbJames Lemieux    public Weekdays.Order getWeekdayOrder() {
719458aa8b4ebb8b7c6fdc0680a1b687ea21a61bf35James Lemieux        enforceMainLooper();
720ff17acface9b98eba868fff0e2d70ddc85c5e4dbJames Lemieux        return mSettingsModel.getWeekdayOrder();
721458aa8b4ebb8b7c6fdc0680a1b687ea21a61bf35James Lemieux    }
722458aa8b4ebb8b7c6fdc0680a1b687ea21a61bf35James Lemieux
723458aa8b4ebb8b7c6fdc0680a1b687ea21a61bf35James Lemieux    /**
724d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     * Used to execute a delegate runnable and track its completion.
725d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan     */
726d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    private static class ExecutedRunnable implements Runnable {
727d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
728d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        private final Runnable mDelegate;
729d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        private boolean mExecuted;
730d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
731d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        private ExecutedRunnable(Runnable delegate) {
732d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            this.mDelegate = delegate;
733d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        }
734d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
735d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        @Override
736d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        public void run() {
737d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            mDelegate.run();
738d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
739d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            synchronized (this) {
740d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan                mExecuted = true;
741d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan                notifyAll();
742d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            }
743d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        }
744d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan
745d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        private boolean isExecuted() {
746d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan            return mExecuted;
747d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan        }
748d6c134979a65c717aa5e667c39d9f9a788b32a45Dylan Phan    }
749ae9a055482865a70028fc9940c5d871775f9620cJustin Klaassen}
750