blob: e5c78e4c5439a8e1ceda5862a3bc923a671b671d [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.deskclock.data;
import static android.content.Context.AUDIO_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.media.AudioManager.FLAG_SHOW_UI;
import static android.media.AudioManager.STREAM_ALARM;
import static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS;
import static android.provider.Settings.ACTION_SOUND_SETTINGS;
import static android.provider.Settings.EXTRA_APP_PACKAGE;
import static com.android.deskclock.Utils.enforceMainLooper;
import static com.android.deskclock.Utils.enforceNotMainLooper;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import androidx.annotation.Keep;
import androidx.annotation.StringRes;
import com.android.deskclock.Predicate;
import com.android.deskclock.R;
import com.android.deskclock.timer.TimerService;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import java.util.ArrayList;
import java.util.List;
/**
* All application-wide data is accessible through this singleton.
*/
public final class DataModel {
/** Indicates the display style of clocks. */
public enum ClockStyle {ANALOG, DIGITAL}
/** Indicates the preferred sort order of cities. */
public enum CitySort {NAME, UTC_OFFSET}
/** Indicates the preferred behavior of hardware volume buttons when firing alarms. */
public enum AlarmVolumeButtonBehavior {NOTHING, SNOOZE, DISMISS}
/** Indicates the reason alarms may not fire or may fire silently. */
public enum SilentSetting {
DO_NOT_DISTURB(R.string.alarms_blocked_by_dnd, 0, Predicate.FALSE, null),
MUTED_VOLUME(R.string.alarm_volume_muted,
R.string.unmute_alarm_volume,
Predicate.TRUE,
new UnmuteAlarmVolumeListener()),
SILENT_RINGTONE(R.string.silent_default_alarm_ringtone,
R.string.change_setting_action,
new ChangeSoundActionPredicate(),
new ChangeSoundSettingsListener()),
BLOCKED_NOTIFICATIONS(R.string.app_notifications_blocked,
R.string.change_setting_action,
Predicate.TRUE,
new ChangeAppNotificationSettingsListener());
private final @StringRes int mLabelResId;
private final @StringRes int mActionResId;
private final Predicate<Context> mActionEnabled;
private final View.OnClickListener mActionListener;
SilentSetting(int labelResId, int actionResId, Predicate<Context> actionEnabled,
View.OnClickListener actionListener) {
mLabelResId = labelResId;
mActionResId = actionResId;
mActionEnabled = actionEnabled;
mActionListener = actionListener;
}
public @StringRes int getLabelResId() { return mLabelResId; }
public @StringRes int getActionResId() { return mActionResId; }
public View.OnClickListener getActionListener() { return mActionListener; }
public boolean isActionEnabled(Context context) {
return mLabelResId != 0 && mActionEnabled.apply(context);
}
private static class UnmuteAlarmVolumeListener implements View.OnClickListener {
@Override
public void onClick(View v) {
// Set the alarm volume to 11/16th of max and show the slider UI.
// 11/16th of max is the initial volume of the alarm stream on a fresh install.
final Context context = v.getContext();
final AudioManager am = (AudioManager) context.getSystemService(AUDIO_SERVICE);
final int index = Math.round(am.getStreamMaxVolume(STREAM_ALARM) * 11f / 16f);
am.setStreamVolume(STREAM_ALARM, index, FLAG_SHOW_UI);
}
}
private static class ChangeSoundSettingsListener implements View.OnClickListener {
@Override
public void onClick(View v) {
final Context context = v.getContext();
context.startActivity(new Intent(ACTION_SOUND_SETTINGS)
.addFlags(FLAG_ACTIVITY_NEW_TASK));
}
}
private static class ChangeSoundActionPredicate implements Predicate<Context> {
@Override
public boolean apply(Context context) {
final Intent intent = new Intent(ACTION_SOUND_SETTINGS);
return intent.resolveActivity(context.getPackageManager()) != null;
}
}
private static class ChangeAppNotificationSettingsListener implements View.OnClickListener {
@Override
public void onClick(View v) {
final Context context = v.getContext();
try {
// Attempt to open the notification settings for this app.
context.startActivity(
new Intent("android.settings.APP_NOTIFICATION_SETTINGS")
.putExtra(EXTRA_APP_PACKAGE, context.getPackageName())
.putExtra("app_uid", context.getApplicationInfo().uid)
.addFlags(FLAG_ACTIVITY_NEW_TASK));
return;
} catch (Exception ignored) {
// best attempt only; recovery code below
}
// Fall back to opening the app settings page.
context.startActivity(new Intent(ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.fromParts("package", context.getPackageName(), null))
.addFlags(FLAG_ACTIVITY_NEW_TASK));
}
}
}
public static final String ACTION_WORLD_CITIES_CHANGED =
"com.android.deskclock.WORLD_CITIES_CHANGED";
/** The single instance of this data model that exists for the life of the application. */
private static final DataModel sDataModel = new DataModel();
private Handler mHandler;
private Context mContext;
/** The model from which settings are fetched. */
private SettingsModel mSettingsModel;
/** The model from which city data are fetched. */
private CityModel mCityModel;
/** The model from which timer data are fetched. */
private TimerModel mTimerModel;
/** The model from which alarm data are fetched. */
private AlarmModel mAlarmModel;
/** The model from which widget data are fetched. */
private WidgetModel mWidgetModel;
/** The model from which data about settings that silence alarms are fetched. */
private SilentSettingsModel mSilentSettingsModel;
/** The model from which stopwatch data are fetched. */
private StopwatchModel mStopwatchModel;
/** The model from which notification data are fetched. */
private NotificationModel mNotificationModel;
/** The model from which time data are fetched. */
private TimeModel mTimeModel;
/** The model from which ringtone data are fetched. */
private RingtoneModel mRingtoneModel;
public static DataModel getDataModel() {
return sDataModel;
}
private DataModel() {}
/**
* Initializes the data model with the context and shared preferences to be used.
*/
public void init(Context context, SharedPreferences prefs) {
if (mContext != context) {
mContext = context.getApplicationContext();
mTimeModel = new TimeModel(mContext);
mWidgetModel = new WidgetModel(prefs);
mNotificationModel = new NotificationModel();
mRingtoneModel = new RingtoneModel(mContext, prefs);
mSettingsModel = new SettingsModel(mContext, prefs, mTimeModel);
mCityModel = new CityModel(mContext, prefs, mSettingsModel);
mAlarmModel = new AlarmModel(mContext, mSettingsModel);
mSilentSettingsModel = new SilentSettingsModel(mContext, mNotificationModel);
mStopwatchModel = new StopwatchModel(mContext, prefs, mNotificationModel);
mTimerModel = new TimerModel(mContext, prefs, mSettingsModel, mRingtoneModel,
mNotificationModel);
}
}
/**
* Convenience for {@code run(runnable, 0)}, i.e. waits indefinitely.
*/
public void run(Runnable runnable) {
try {
run(runnable, 0 /* waitMillis */);
} catch (InterruptedException ignored) {
}
}
/**
* Updates all timers and the stopwatch after the device has shutdown and restarted.
*/
public void updateAfterReboot() {
enforceMainLooper();
mTimerModel.updateTimersAfterReboot();
mStopwatchModel.setStopwatch(getStopwatch().updateAfterReboot());
}
/**
* Updates all timers and the stopwatch after the device's time has changed.
*/
public void updateAfterTimeSet() {
enforceMainLooper();
mTimerModel.updateTimersAfterTimeSet();
mStopwatchModel.setStopwatch(getStopwatch().updateAfterTimeSet());
}
/**
* Posts a runnable to the main thread and blocks until the runnable executes. Used to access
* the data model from the main thread.
*/
public void run(Runnable runnable, long waitMillis) throws InterruptedException {
if (Looper.myLooper() == Looper.getMainLooper()) {
runnable.run();
return;
}
final ExecutedRunnable er = new ExecutedRunnable(runnable);
getHandler().post(er);
// Wait for the data to arrive, if it has not.
synchronized (er) {
if (!er.isExecuted()) {
er.wait(waitMillis);
}
}
}
/**
* @return a handler associated with the main thread
*/
private synchronized Handler getHandler() {
if (mHandler == null) {
mHandler = new Handler(Looper.getMainLooper());
}
return mHandler;
}
//
// Application
//
/**
* @param inForeground {@code true} to indicate the application is open in the foreground
*/
public void setApplicationInForeground(boolean inForeground) {
enforceMainLooper();
if (mNotificationModel.isApplicationInForeground() != inForeground) {
mNotificationModel.setApplicationInForeground(inForeground);
// Refresh all notifications in response to a change in app open state.
mTimerModel.updateNotification();
mTimerModel.updateMissedNotification();
mStopwatchModel.updateNotification();
mSilentSettingsModel.updateSilentState();
}
}
/**
* @return {@code true} when the application is open in the foreground; {@code false} otherwise
*/
public boolean isApplicationInForeground() {
enforceMainLooper();
return mNotificationModel.isApplicationInForeground();
}
/**
* Called when the notifications may be stale or absent from the notification manager and must
* be rebuilt. e.g. after upgrading the application
*/
public void updateAllNotifications() {
enforceMainLooper();
mTimerModel.updateNotification();
mTimerModel.updateMissedNotification();
mStopwatchModel.updateNotification();
}
//
// Cities
//
/**
* @return a list of all cities in their display order
*/
public List<City> getAllCities() {
enforceMainLooper();
return mCityModel.getAllCities();
}
/**
* @return a city representing the user's home timezone
*/
public City getHomeCity() {
enforceMainLooper();
return mCityModel.getHomeCity();
}
/**
* @return a list of cities not selected for display
*/
public List<City> getUnselectedCities() {
enforceMainLooper();
return mCityModel.getUnselectedCities();
}
/**
* @return a list of cities selected for display
*/
public List<City> getSelectedCities() {
enforceMainLooper();
return mCityModel.getSelectedCities();
}
/**
* @param cities the new collection of cities selected for display by the user
*/
public void setSelectedCities(Collection<City> cities) {
enforceMainLooper();
mCityModel.setSelectedCities(cities);
}
/**
* @return a comparator used to locate index positions
*/
public Comparator<City> getCityIndexComparator() {
enforceMainLooper();
return mCityModel.getCityIndexComparator();
}
/**
* @return the order in which cities are sorted
*/
public CitySort getCitySort() {
enforceMainLooper();
return mCityModel.getCitySort();
}
/**
* Adjust the order in which cities are sorted.
*/
public void toggleCitySort() {
enforceMainLooper();
mCityModel.toggleCitySort();
}
/**
* @param cityListener listener to be notified when the world city list changes
*/
public void addCityListener(CityListener cityListener) {
enforceMainLooper();
mCityModel.addCityListener(cityListener);
}
/**
* @param cityListener listener that no longer needs to be notified of world city list changes
*/
public void removeCityListener(CityListener cityListener) {
enforceMainLooper();
mCityModel.removeCityListener(cityListener);
}
//
// Timers
//
/**
* @param timerListener to be notified when timers are added, updated and removed
*/
public void addTimerListener(TimerListener timerListener) {
enforceMainLooper();
mTimerModel.addTimerListener(timerListener);
}
/**
* @param timerListener to no longer be notified when timers are added, updated and removed
*/
public void removeTimerListener(TimerListener timerListener) {
enforceMainLooper();
mTimerModel.removeTimerListener(timerListener);
}
/**
* @return a list of timers for display
*/
public List<Timer> getTimers() {
enforceMainLooper();
return mTimerModel.getTimers();
}
/**
* @return a list of expired timers for display
*/
public List<Timer> getExpiredTimers() {
enforceMainLooper();
return mTimerModel.getExpiredTimers();
}
/**
* @param timerId identifies the timer to return
* @return the timer with the given {@code timerId}
*/
public Timer getTimer(int timerId) {
enforceMainLooper();
return mTimerModel.getTimer(timerId);
}
/**
* @param length the length of the timer in milliseconds
* @param label describes the purpose of the timer
* @param deleteAfterUse {@code true} indicates the timer should be deleted when it is reset
* @return the newly added timer
*/
public Timer addTimer(long length, String label, boolean deleteAfterUse) {
enforceMainLooper();
return mTimerModel.addTimer(length, label, deleteAfterUse);
}
/**
* @param timer the timer to be removed
*/
public void removeTimer(Timer timer) {
enforceMainLooper();
mTimerModel.removeTimer(timer);
}
/**
* @param timer the timer to be started
*/
public void startTimer(Timer timer) {
startTimer(null, timer);
}
/**
* @param service used to start foreground notifications for expired timers
* @param timer the timer to be started
*/
public void startTimer(Service service, Timer timer) {
enforceMainLooper();
final Timer started = timer.start();
mTimerModel.updateTimer(started);
if (timer.getRemainingTime() <= 0) {
if (service != null) {
expireTimer(service, started);
} else {
mContext.startService(TimerService.createTimerExpiredIntent(mContext, started));
}
}
}
/**
* @param timer the timer to be paused
*/
public void pauseTimer(Timer timer) {
enforceMainLooper();
mTimerModel.updateTimer(timer.pause());
}
/**
* @param service used to start foreground notifications for expired timers
* @param timer the timer to be expired
*/
public void expireTimer(Service service, Timer timer) {
enforceMainLooper();
mTimerModel.expireTimer(service, timer);
}
/**
* @param timer the timer to be reset
*/
@Keep
public void resetTimer(Timer timer) {
enforceMainLooper();
mTimerModel.resetTimer(timer, false /* allowDelete */, 0 /* eventLabelId */);
}
/**
* If the given {@code timer} is expired and marked for deletion after use then this method
* removes the the timer. The timer is otherwise transitioned to the reset state and continues
* to exist.
*
* @param timer the timer to be reset
* @param eventLabelId the label of the timer event to send; 0 if no event should be sent
*/
public void resetOrDeleteTimer(Timer timer, @StringRes int eventLabelId) {
enforceMainLooper();
mTimerModel.resetTimer(timer, true /* allowDelete */, eventLabelId);
}
/**
* Resets all expired timers.
*
* @param eventLabelId the label of the timer event to send; 0 if no event should be sent
*/
public void resetOrDeleteExpiredTimers(@StringRes int eventLabelId) {
enforceMainLooper();
mTimerModel.resetOrDeleteExpiredTimers(eventLabelId);
}
/**
* Resets all unexpired timers.
*
* @param eventLabelId the label of the timer event to send; 0 if no event should be sent
*/
public void resetUnexpiredTimers(@StringRes int eventLabelId) {
enforceMainLooper();
mTimerModel.resetUnexpiredTimers(eventLabelId);
}
/**
* Resets all missed timers.
*
* @param eventLabelId the label of the timer event to send; 0 if no event should be sent
*/
public void resetMissedTimers(@StringRes int eventLabelId) {
enforceMainLooper();
mTimerModel.resetMissedTimers(eventLabelId);
}
/**
* @param timer the timer to which a minute should be added to the remaining time
*/
public void addTimerMinute(Timer timer) {
enforceMainLooper();
mTimerModel.updateTimer(timer.addMinute());
}
/**
* @param timer the timer to which the new {@code label} belongs
* @param label the new label to store for the {@code timer}
*/
public void setTimerLabel(Timer timer, String label) {
enforceMainLooper();
mTimerModel.updateTimer(timer.setLabel(label));
}
/**
* Updates the timer notifications to be current.
*/
public void updateTimerNotification() {
enforceMainLooper();
mTimerModel.updateNotification();
}
/**
* @return the uri of the default ringtone to play for all timers when no user selection exists
*/
public Uri getDefaultTimerRingtoneUri() {
enforceMainLooper();
return mTimerModel.getDefaultTimerRingtoneUri();
}
/**
* @return {@code true} iff the ringtone to play for all timers is the silent ringtone
*/
public boolean isTimerRingtoneSilent() {
enforceMainLooper();
return mTimerModel.isTimerRingtoneSilent();
}
/**
* @return the uri of the ringtone to play for all timers
*/
public Uri getTimerRingtoneUri() {
enforceMainLooper();
return mTimerModel.getTimerRingtoneUri();
}
/**
* @param uri the uri of the ringtone to play for all timers
*/
public void setTimerRingtoneUri(Uri uri) {
enforceMainLooper();
mTimerModel.setTimerRingtoneUri(uri);
}
/**
* @return the title of the ringtone that is played for all timers
*/
public String getTimerRingtoneTitle() {
enforceMainLooper();
return mTimerModel.getTimerRingtoneTitle();
}
/**
* @return the duration, in milliseconds, of the crescendo to apply to timer ringtone playback;
* {@code 0} implies no crescendo should be applied
*/
public long getTimerCrescendoDuration() {
enforceMainLooper();
return mTimerModel.getTimerCrescendoDuration();
}
/**
* @return whether vibrate is enabled for all timers.
*/
public boolean getTimerVibrate() {
enforceMainLooper();
return mTimerModel.getTimerVibrate();
}
/**
* @param enabled whether vibrate is enabled for all timers.
*/
public void setTimerVibrate(boolean enabled) {
enforceMainLooper();
mTimerModel.setTimerVibrate(enabled);
}
//
// Alarms
//
/**
* @return the uri of the ringtone to which all new alarms default
*/
public Uri getDefaultAlarmRingtoneUri() {
enforceMainLooper();
return mAlarmModel.getDefaultAlarmRingtoneUri();
}
/**
* @param uri the uri of the ringtone to which future new alarms will default
*/
public void setDefaultAlarmRingtoneUri(Uri uri) {
enforceMainLooper();
mAlarmModel.setDefaultAlarmRingtoneUri(uri);
}
/**
* @return the duration, in milliseconds, of the crescendo to apply to alarm ringtone playback;
* {@code 0} implies no crescendo should be applied
*/
public long getAlarmCrescendoDuration() {
enforceMainLooper();
return mAlarmModel.getAlarmCrescendoDuration();
}
/**
* @return the behavior to execute when volume buttons are pressed while firing an alarm
*/
public AlarmVolumeButtonBehavior getAlarmVolumeButtonBehavior() {
enforceMainLooper();
return mAlarmModel.getAlarmVolumeButtonBehavior();
}
/**
* @return the number of minutes an alarm may ring before it has timed out and becomes missed
*/
public int getAlarmTimeout() {
return mAlarmModel.getAlarmTimeout();
}
/**
* @return the number of minutes an alarm will remain snoozed before it rings again
*/
public int getSnoozeLength() {
return mAlarmModel.getSnoozeLength();
}
public int getFlipAction() {
return mAlarmModel.getFlipAction();
}
public int getShakeAction() {
return mAlarmModel.getShakeAction();
}
//
// Stopwatch
//
/**
* @param stopwatchListener to be notified when stopwatch changes or laps are added
*/
public void addStopwatchListener(StopwatchListener stopwatchListener) {
enforceMainLooper();
mStopwatchModel.addStopwatchListener(stopwatchListener);
}
/**
* @param stopwatchListener to no longer be notified when stopwatch changes or laps are added
*/
public void removeStopwatchListener(StopwatchListener stopwatchListener) {
enforceMainLooper();
mStopwatchModel.removeStopwatchListener(stopwatchListener);
}
/**
* @return the current state of the stopwatch
*/
public Stopwatch getStopwatch() {
enforceMainLooper();
return mStopwatchModel.getStopwatch();
}
/**
*/
public void startStopwatch() {
enforceMainLooper();
mStopwatchModel.setStopwatch(getStopwatch().start());
}
/**
*/
public void pauseStopwatch() {
enforceMainLooper();
mStopwatchModel.setStopwatch(getStopwatch().pause());
}
/**
*/
public void resetStopwatch() {
enforceMainLooper();
mStopwatchModel.setStopwatch(getStopwatch().reset());
}
/**
* @return the laps recorded for this stopwatch
*/
public List<Lap> getLaps() {
enforceMainLooper();
return (mStopwatchModel != null) ? mStopwatchModel.getLaps() : new ArrayList<>();
}
/**
* @return a newly recorded lap completed now; {@code null} if no more laps can be added
*/
public Lap addLap() {
enforceMainLooper();
return mStopwatchModel.addLap();
}
/**
* @return {@code true} iff more laps can be recorded
*/
public boolean canAddMoreLaps() {
enforceMainLooper();
return mStopwatchModel.canAddMoreLaps();
}
/**
* @return the longest lap time of all recorded laps and the current lap
*/
public long getLongestLapTime() {
enforceMainLooper();
return mStopwatchModel.getLongestLapTime();
}
/**
* @param time a point in time after the end of the last lap
* @return the elapsed time between the given {@code time} and the end of the previous lap
*/
public long getCurrentLapTime(long time) {
enforceMainLooper();
return mStopwatchModel.getCurrentLapTime(time);
}
//
// Time
// (Time settings/values are accessible from any Thread so no Thread-enforcement exists.)
//
/**
* @return the current time in milliseconds
*/
public long currentTimeMillis() {
return mTimeModel.currentTimeMillis();
}
/**
* @return milliseconds since boot, including time spent in sleep
*/
public long elapsedRealtime() {
return mTimeModel.elapsedRealtime();
}
/**
* @return {@code true} if 24 hour time format is selected; {@code false} otherwise
*/
public boolean is24HourFormat() {
return mTimeModel.is24HourFormat();
}
/**
* @return a new calendar object initialized to the {@link #currentTimeMillis()}
*/
public Calendar getCalendar() {
return mTimeModel.getCalendar();
}
//
// Ringtones
//
/**
* Ringtone titles are cached because loading them is expensive. This method
* <strong>must</strong> be called on a background thread and is responsible for priming the
* cache of ringtone titles to avoid later fetching titles on the main thread.
*/
public void loadRingtoneTitles() {
enforceNotMainLooper();
mRingtoneModel.loadRingtoneTitles();
}
/**
* Recheck the permission to read each custom ringtone.
*/
public void loadRingtonePermissions() {
enforceNotMainLooper();
mRingtoneModel.loadRingtonePermissions();
}
/**
* @param uri the uri of a ringtone
* @return the title of the ringtone with the {@code uri}; {@code null} if it cannot be fetched
*/
public String getRingtoneTitle(Uri uri) {
enforceMainLooper();
return mRingtoneModel.getRingtoneTitle(uri);
}
/**
* @param uri the uri of an audio file to use as a ringtone
* @param title the title of the audio content at the given {@code uri}
*/
public void addCustomRingtone(Uri uri, String title) {
enforceMainLooper();
mRingtoneModel.addCustomRingtone(uri, title);
}
/**
* @param uri identifies the ringtone to remove
*/
public void removeCustomRingtone(Uri uri) {
enforceMainLooper();
mRingtoneModel.removeCustomRingtone(uri);
}
/**
* @return all available custom ringtones
*/
public List<CustomRingtone> getCustomRingtones() {
enforceMainLooper();
return mRingtoneModel.getCustomRingtones();
}
//
// Widgets
//
/**
* @param widgetClass indicates the type of widget being counted
* @param count the number of widgets of the given type
* @param eventCategoryId identifies the category of event to send
*/
public void updateWidgetCount(Class widgetClass, int count, @StringRes int eventCategoryId) {
enforceMainLooper();
mWidgetModel.updateWidgetCount(widgetClass, count, eventCategoryId);
}
//
// Settings
//
/**
* @param silentSettingsListener to be notified when alarm-silencing settings change
*/
public void addSilentSettingsListener(OnSilentSettingsListener silentSettingsListener) {
enforceMainLooper();
mSilentSettingsModel.addSilentSettingsListener(silentSettingsListener);
}
/**
* @param silentSettingsListener to no longer be notified when alarm-silencing settings change
*/
public void removeSilentSettingsListener(OnSilentSettingsListener silentSettingsListener) {
enforceMainLooper();
mSilentSettingsModel.removeSilentSettingsListener(silentSettingsListener);
}
/**
* @return the id used to discriminate relevant AlarmManager callbacks from defunct ones
*/
public int getGlobalIntentId() {
return mSettingsModel.getGlobalIntentId();
}
/**
* Update the id used to discriminate relevant AlarmManager callbacks from defunct ones
*/
public void updateGlobalIntentId() {
enforceMainLooper();
mSettingsModel.updateGlobalIntentId();
}
/**
* @return the style of clock to display in the clock application
*/
public ClockStyle getClockStyle() {
enforceMainLooper();
return mSettingsModel.getClockStyle();
}
/**
* @return the style of clock to display in the clock application
*/
public boolean getDisplayClockSeconds() {
enforceMainLooper();
return mSettingsModel.getDisplayClockSeconds();
}
/**
* @param displaySeconds whether or not to display seconds for main clock
*/
public void setDisplayClockSeconds(boolean displaySeconds) {
enforceMainLooper();
mSettingsModel.setDisplayClockSeconds(displaySeconds);
}
/**
* @return the style of clock to display in the clock screensaver
*/
public ClockStyle getScreensaverClockStyle() {
enforceMainLooper();
return mSettingsModel.getScreensaverClockStyle();
}
/**
* @return {@code true} if the screen saver should be dimmed for lower contrast at night
*/
public boolean getScreensaverNightModeOn() {
enforceMainLooper();
return mSettingsModel.getScreensaverNightModeOn();
}
/**
* @return {@code true} if the users wants to automatically show a clock for their home timezone
* when they have travelled outside of that timezone
*/
public boolean getShowHomeClock() {
enforceMainLooper();
return mSettingsModel.getShowHomeClock();
}
/**
* @return the display order of the weekdays, which can start with {@link Calendar#SATURDAY},
* {@link Calendar#SUNDAY} or {@link Calendar#MONDAY}
*/
public Weekdays.Order getWeekdayOrder() {
enforceMainLooper();
return mSettingsModel.getWeekdayOrder();
}
/**
* @return {@code true} if the restore process (of backup and restore) has completed
*/
public boolean isRestoreBackupFinished() {
return mSettingsModel.isRestoreBackupFinished();
}
/**
* @param finished {@code true} means the restore process (of backup and restore) has completed
*/
public void setRestoreBackupFinished(boolean finished) {
mSettingsModel.setRestoreBackupFinished(finished);
}
/**
* @return a description of the time zones available for selection
*/
public TimeZones getTimeZones() {
enforceMainLooper();
return mSettingsModel.getTimeZones();
}
/**
* Used to execute a delegate runnable and track its completion.
*/
private static class ExecutedRunnable implements Runnable {
private final Runnable mDelegate;
private boolean mExecuted;
private ExecutedRunnable(Runnable delegate) {
this.mDelegate = delegate;
}
@Override
public void run() {
mDelegate.run();
synchronized (this) {
mExecuted = true;
notifyAll();
}
}
private boolean isExecuted() {
return mExecuted;
}
}
}