• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.deskclock.data
18 
19 import android.app.Service
20 import android.content.Context
21 import android.content.Context.AUDIO_SERVICE
22 import android.content.Intent
23 import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
24 import android.content.SharedPreferences
25 import android.media.AudioManager
26 import android.media.AudioManager.FLAG_SHOW_UI
27 import android.media.AudioManager.STREAM_ALARM
28 import android.net.Uri
29 import android.os.Handler
30 import android.os.Looper
31 import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
32 import android.provider.Settings.ACTION_SOUND_SETTINGS
33 import android.view.View
34 import androidx.annotation.Keep
35 import androidx.annotation.StringRes
36 
37 import com.android.deskclock.Predicate
38 import com.android.deskclock.R
39 import com.android.deskclock.Utils
40 import com.android.deskclock.timer.TimerService
41 
42 import java.util.Calendar
43 
44 import kotlin.Comparator
45 import kotlin.math.roundToInt
46 
47 /**
48  * All application-wide data is accessible through this singleton.
49  */
50 class DataModel private constructor() {
51 
52     /** Indicates the display style of clocks.  */
53     enum class ClockStyle {
54         ANALOG, DIGITAL
55     }
56 
57     /** Indicates the preferred sort order of cities.  */
58     enum class CitySort {
59         NAME, UTC_OFFSET
60     }
61 
62     /** Indicates the preferred behavior of hardware volume buttons when firing alarms.  */
63     enum class AlarmVolumeButtonBehavior {
64         NOTHING, SNOOZE, DISMISS
65     }
66 
67     /** Indicates the reason alarms may not fire or may fire silently.  */
68     enum class SilentSetting(
69         @field:StringRes @get:StringRes val labelResId: Int,
70         @field:StringRes @get:StringRes val actionResId: Int,
71         private val mActionEnabled: Predicate<Context>,
72         private val mActionListener: View.OnClickListener?
73     ) {
74 
75         DO_NOT_DISTURB(R.string.alarms_blocked_by_dnd,
76                 0,
77                 Predicate.FALSE as Predicate<Context>,
78                 mActionListener = null),
79         MUTED_VOLUME(R.string.alarm_volume_muted,
80                 R.string.unmute_alarm_volume,
81                 Predicate.TRUE as Predicate<Context>,
82                 UnmuteAlarmVolumeListener()),
83         SILENT_RINGTONE(R.string.silent_default_alarm_ringtone,
84                 R.string.change_setting_action,
85                 ChangeSoundActionPredicate(),
86                 ChangeSoundSettingsListener()),
87         BLOCKED_NOTIFICATIONS(R.string.app_notifications_blocked,
88                 R.string.change_setting_action,
89                 Predicate.TRUE as Predicate<Context>,
90                 ChangeAppNotificationSettingsListener());
91 
92         val actionListener: View.OnClickListener?
93             get() = mActionListener
94 
isActionEnablednull95         fun isActionEnabled(context: Context): Boolean {
96             return labelResId != 0 && mActionEnabled.apply(context)
97         }
98 
99         private class UnmuteAlarmVolumeListener : View.OnClickListener {
onClicknull100             override fun onClick(v: View) {
101                 // Set the alarm volume to 11/16th of max and show the slider UI.
102                 // 11/16th of max is the initial volume of the alarm stream on a fresh install.
103                 val context: Context = v.context
104                 val am: AudioManager = context.getSystemService(AUDIO_SERVICE) as AudioManager
105                 val index = (am.getStreamMaxVolume(STREAM_ALARM) * 11f / 16f).roundToInt()
106                 am.setStreamVolume(STREAM_ALARM, index, FLAG_SHOW_UI)
107             }
108         }
109 
110         private class ChangeSoundSettingsListener : View.OnClickListener {
onClicknull111             override fun onClick(v: View) {
112                 val context: Context = v.context
113                 context.startActivity(Intent(ACTION_SOUND_SETTINGS)
114                         .addFlags(FLAG_ACTIVITY_NEW_TASK))
115             }
116         }
117 
118         private class ChangeSoundActionPredicate : Predicate<Context> {
applynull119             override fun apply(context: Context): Boolean {
120                 val intent = Intent(ACTION_SOUND_SETTINGS)
121                 return intent.resolveActivity(context.packageManager) != null
122             }
123         }
124 
125         private class ChangeAppNotificationSettingsListener : View.OnClickListener {
onClicknull126             override fun onClick(v: View) {
127                 val context: Context = v.context
128                 if (Utils.isLOrLater) {
129                     try {
130                         // Attempt to open the notification settings for this app.
131                         context.startActivity(
132                                 Intent("android.settings.APP_NOTIFICATION_SETTINGS")
133                                         .putExtra("app_package", context.packageName)
134                                         .putExtra("app_uid", context.applicationInfo.uid)
135                                         .addFlags(FLAG_ACTIVITY_NEW_TASK))
136                         return
137                     } catch (ignored: Exception) {
138                         // best attempt only; recovery code below
139                     }
140                 }
141 
142                 // Fall back to opening the app settings page.
143                 context.startActivity(Intent(ACTION_APPLICATION_DETAILS_SETTINGS)
144                         .setData(Uri.fromParts("package", context.packageName, null))
145                         .addFlags(FLAG_ACTIVITY_NEW_TASK))
146             }
147         }
148     }
149 
150     private var mHandler: Handler? = null
151     private var mContext: Context? = null
152 
153     /** The model from which settings are fetched.  */
154     private var mSettingsModel: SettingsModel? = null
155 
156     /** The model from which city data are fetched.  */
157     private var mCityModel: CityModel? = null
158 
159     /** The model from which timer data are fetched.  */
160     private var mTimerModel: TimerModel? = null
161 
162     /** The model from which alarm data are fetched.  */
163     private var mAlarmModel: AlarmModel? = null
164 
165     /** The model from which widget data are fetched.  */
166     private var mWidgetModel: WidgetModel? = null
167 
168     /** The model from which data about settings that silence alarms are fetched.  */
169     private var mSilentSettingsModel: SilentSettingsModel? = null
170 
171     /** The model from which stopwatch data are fetched.  */
172     private var mStopwatchModel: StopwatchModel? = null
173 
174     /** The model from which notification data are fetched.  */
175     private var mNotificationModel: NotificationModel? = null
176 
177     /** The model from which time data are fetched.  */
178     private var mTimeModel: TimeModel? = null
179 
180     /** The model from which ringtone data are fetched.  */
181     private var mRingtoneModel: RingtoneModel? = null
182 
183     /**
184      * Initializes the data model with the context and shared preferences to be used.
185      */
initnull186     fun init(context: Context, prefs: SharedPreferences) {
187         if (mContext !== context) {
188             mContext = context.applicationContext
189             mTimeModel = TimeModel(mContext!!)
190             mWidgetModel = WidgetModel(prefs)
191             mNotificationModel = NotificationModel()
192             mRingtoneModel = RingtoneModel(mContext!!, prefs)
193             mSettingsModel = SettingsModel(mContext!!, prefs, mTimeModel!!)
194             mCityModel = CityModel(mContext!!, prefs, mSettingsModel!!)
195             mAlarmModel = AlarmModel(mContext!!, mSettingsModel!!)
196             mSilentSettingsModel = SilentSettingsModel(mContext!!, mNotificationModel!!)
197             mStopwatchModel = StopwatchModel(mContext!!, prefs, mNotificationModel!!)
198             mTimerModel = TimerModel(mContext!!, prefs, mSettingsModel!!, mRingtoneModel!!,
199                     mNotificationModel!!)
200         }
201     }
202 
203     /**
204      * Convenience for `run(runnable, 0)`, i.e. waits indefinitely.
205      */
runnull206     fun run(runnable: Runnable) {
207         try {
208             run(runnable, 0 /* waitMillis */)
209         } catch (ignored: InterruptedException) {
210         }
211     }
212 
213     /**
214      * Updates all timers and the stopwatch after the device has shutdown and restarted.
215      */
updateAfterRebootnull216     fun updateAfterReboot() {
217         Utils.enforceMainLooper()
218         mTimerModel!!.updateTimersAfterReboot()
219         mStopwatchModel!!.setStopwatch(stopwatch.updateAfterReboot())
220     }
221 
222     /**
223      * Updates all timers and the stopwatch after the device's time has changed.
224      */
updateAfterTimeSetnull225     fun updateAfterTimeSet() {
226         Utils.enforceMainLooper()
227         mTimerModel!!.updateTimersAfterTimeSet()
228         mStopwatchModel!!.setStopwatch(stopwatch.updateAfterTimeSet())
229     }
230 
231     /**
232      * Posts a runnable to the main thread and blocks until the runnable executes. Used to access
233      * the data model from the main thread.
234      */
235     @Throws(InterruptedException::class)
runnull236     fun run(runnable: Runnable, waitMillis: Long) {
237         if (Looper.myLooper() === Looper.getMainLooper()) {
238             runnable.run()
239             return
240         }
241 
242         val er = ExecutedRunnable(runnable)
243         handler.post(er)
244 
245         // Wait for the data to arrive, if it has not.
246         synchronized(er) {
247             if (!er.isExecuted) {
248                 er.wait(waitMillis)
249             }
250         }
251     }
252 
253     /**
254      * @return a handler associated with the main thread
255      */
256     @get:Synchronized
257     private val handler: Handler
258         get() {
259             if (mHandler == null) {
260                 mHandler = Handler(Looper.getMainLooper())
261             }
262             return mHandler!!
263         }
264 
265     //
266     // Application
267     //
268 
269     var isApplicationInForeground: Boolean
270         /**
271          * @return `true` when the application is open in the foreground; `false` otherwise
272          */
273         get() {
274             Utils.enforceMainLooper()
275             return mNotificationModel!!.isApplicationInForeground
276         }
277         /**
278          * @param inForeground `true` to indicate the application is open in the foreground
279          */
280         set(inForeground) {
281             Utils.enforceMainLooper()
282             if (mNotificationModel!!.isApplicationInForeground != inForeground) {
283                 mNotificationModel!!.isApplicationInForeground = inForeground
284 
285                 // Refresh all notifications in response to a change in app open state.
286                 mTimerModel!!.updateNotification()
287                 mTimerModel!!.updateMissedNotification()
288                 mStopwatchModel!!.updateNotification()
289                 mSilentSettingsModel!!.updateSilentState()
290             }
291         }
292 
293     /**
294      * Called when the notifications may be stale or absent from the notification manager and must
295      * be rebuilt. e.g. after upgrading the application
296      */
updateAllNotificationsnull297     fun updateAllNotifications() {
298         Utils.enforceMainLooper()
299         mTimerModel!!.updateNotification()
300         mTimerModel!!.updateMissedNotification()
301         mStopwatchModel!!.updateNotification()
302     }
303 
304     //
305     // Cities
306     //
307 
308     /**
309      * @return a list of all cities in their display order
310      */
311     val allCities: List<City>
312         get() {
313             Utils.enforceMainLooper()
314             return mCityModel!!.allCities
315         }
316 
317     /**
318      * @return a city representing the user's home timezone
319      */
320     val homeCity: City
321         get() {
322             Utils.enforceMainLooper()
323             return mCityModel!!.homeCity
324         }
325 
326     /**
327      * @return a list of cities not selected for display
328      */
329     val unselectedCities: List<City>
330         get() {
331             Utils.enforceMainLooper()
332             return mCityModel!!.unselectedCities
333         }
334 
335     var selectedCities: Collection<City>
336         /**
337          * @return a list of cities selected for display
338          */
339         get() {
340             Utils.enforceMainLooper()
341             return mCityModel!!.selectedCities
342         }
343         /**
344          * @param cities the new collection of cities selected for display by the user
345          */
346         set(cities) {
347             Utils.enforceMainLooper()
348             mCityModel?.setSelectedCities(cities)
349         }
350 
351     /**
352      * @return a comparator used to locate index positions
353      */
354     val cityIndexComparator: Comparator<City>
355         get() {
356             Utils.enforceMainLooper()
357             return mCityModel!!.cityIndexComparator
358         }
359 
360     /**
361      * @return the order in which cities are sorted
362      */
363     val citySort: CitySort
364         get() {
365             Utils.enforceMainLooper()
366             return mCityModel!!.citySort
367         }
368 
369     /**
370      * Adjust the order in which cities are sorted.
371      */
toggleCitySortnull372     fun toggleCitySort() {
373         Utils.enforceMainLooper()
374         mCityModel?.toggleCitySort()
375     }
376 
377     /**
378      * @param cityListener listener to be notified when the world city list changes
379      */
addCityListenernull380     fun addCityListener(cityListener: CityListener) {
381         Utils.enforceMainLooper()
382         mCityModel?.addCityListener(cityListener)
383     }
384 
385     /**
386      * @param cityListener listener that no longer needs to be notified of world city list changes
387      */
removeCityListenernull388     fun removeCityListener(cityListener: CityListener) {
389         Utils.enforceMainLooper()
390         mCityModel?.removeCityListener(cityListener)
391     }
392 
393     //
394     // Timers
395     //
396 
397     /**
398      * @param timerListener to be notified when timers are added, updated and removed
399      */
addTimerListenernull400     fun addTimerListener(timerListener: TimerListener) {
401         Utils.enforceMainLooper()
402         mTimerModel?.addTimerListener(timerListener)
403     }
404 
405     /**
406      * @param timerListener to no longer be notified when timers are added, updated and removed
407      */
removeTimerListenernull408     fun removeTimerListener(timerListener: TimerListener) {
409         Utils.enforceMainLooper()
410         mTimerModel?.removeTimerListener(timerListener)
411     }
412 
413     /**
414      * @return a list of timers for display
415      */
416     val timers: List<Timer>
417         get() {
418             Utils.enforceMainLooper()
419             return mTimerModel!!.timers
420         }
421 
422     /**
423      * @return a list of expired timers for display
424      */
425     val expiredTimers: List<Timer>
426         get() {
427             Utils.enforceMainLooper()
428             return mTimerModel!!.expiredTimers
429         }
430 
431     /**
432      * @param timerId identifies the timer to return
433      * @return the timer with the given `timerId`
434      */
getTimernull435     fun getTimer(timerId: Int): Timer? {
436         Utils.enforceMainLooper()
437         return mTimerModel?.getTimer(timerId)
438     }
439 
440     /**
441      * @return the timer that last expired and is still expired now; `null` if no timers are
442      * expired
443      */
444     val mostRecentExpiredTimer: Timer?
445         get() {
446             Utils.enforceMainLooper()
447             return mTimerModel?.mostRecentExpiredTimer
448         }
449 
450     /**
451      * @param length the length of the timer in milliseconds
452      * @param label describes the purpose of the timer
453      * @param deleteAfterUse `true` indicates the timer should be deleted when it is reset
454      * @return the newly added timer
455      */
addTimernull456     fun addTimer(length: Long, label: String?, deleteAfterUse: Boolean): Timer {
457         Utils.enforceMainLooper()
458         return mTimerModel!!.addTimer(length, label, deleteAfterUse)
459     }
460 
461     /**
462      * @param timer the timer to be removed
463      */
removeTimernull464     fun removeTimer(timer: Timer) {
465         Utils.enforceMainLooper()
466         mTimerModel?.removeTimer(timer)
467     }
468 
469     /**
470      * @param timer the timer to be started
471      */
startTimernull472     fun startTimer(timer: Timer) {
473         startTimer(null, timer)
474     }
475 
476     /**
477      * @param service used to start foreground notifications for expired timers
478      * @param timer the timer to be started
479      */
startTimernull480     fun startTimer(service: Service?, timer: Timer) {
481         Utils.enforceMainLooper()
482         val started = timer.start()
483         mTimerModel?.updateTimer(started)
484         if (timer.remainingTime <= 0) {
485             if (service != null) {
486                 expireTimer(service, started)
487             } else {
488                 mContext!!.startService(TimerService.createTimerExpiredIntent(mContext!!, started))
489             }
490         }
491     }
492 
493     /**
494      * @param timer the timer to be paused
495      */
pauseTimernull496     fun pauseTimer(timer: Timer) {
497         Utils.enforceMainLooper()
498         mTimerModel?.updateTimer(timer.pause())
499     }
500 
501     /**
502      * @param service used to start foreground notifications for expired timers
503      * @param timer the timer to be expired
504      */
expireTimernull505     fun expireTimer(service: Service?, timer: Timer) {
506         Utils.enforceMainLooper()
507         mTimerModel?.expireTimer(service, timer)
508     }
509 
510     /**
511      * @param timer the timer to be reset
512      * @return the reset `timer`
513      */
514     @Keep
resetTimernull515     fun resetTimer(timer: Timer): Timer? {
516         Utils.enforceMainLooper()
517         return mTimerModel?.resetTimer(timer, false /* allowDelete */, 0 /* eventLabelId */)
518     }
519 
520     /**
521      * If the given `timer` is expired and marked for deletion after use then this method
522      * removes the timer. The timer is otherwise transitioned to the reset state and continues
523      * to exist.
524      *
525      * @param timer the timer to be reset
526      * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
527      * @return the reset `timer` or `null` if the timer was deleted
528      */
resetOrDeleteTimernull529     fun resetOrDeleteTimer(timer: Timer, @StringRes eventLabelId: Int): Timer? {
530         Utils.enforceMainLooper()
531         return mTimerModel?.resetTimer(timer, true /* allowDelete */, eventLabelId)
532     }
533 
534     /**
535      * Resets all expired timers.
536      *
537      * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
538      */
resetOrDeleteExpiredTimersnull539     fun resetOrDeleteExpiredTimers(@StringRes eventLabelId: Int) {
540         Utils.enforceMainLooper()
541         mTimerModel?.resetOrDeleteExpiredTimers(eventLabelId)
542     }
543 
544     /**
545      * Resets all unexpired timers.
546      *
547      * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
548      */
resetUnexpiredTimersnull549     fun resetUnexpiredTimers(@StringRes eventLabelId: Int) {
550         Utils.enforceMainLooper()
551         mTimerModel?.resetUnexpiredTimers(eventLabelId)
552     }
553 
554     /**
555      * Resets all missed timers.
556      *
557      * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
558      */
resetMissedTimersnull559     fun resetMissedTimers(@StringRes eventLabelId: Int) {
560         Utils.enforceMainLooper()
561         mTimerModel?.resetMissedTimers(eventLabelId)
562     }
563 
564     /**
565      * @param timer the timer to which a minute should be added to the remaining time
566      */
addTimerMinutenull567     fun addTimerMinute(timer: Timer) {
568         Utils.enforceMainLooper()
569         mTimerModel?.updateTimer(timer.addMinute())
570     }
571 
572     /**
573      * @param timer the timer to which the new `label` belongs
574      * @param label the new label to store for the `timer`
575      */
setTimerLabelnull576     fun setTimerLabel(timer: Timer, label: String?) {
577         Utils.enforceMainLooper()
578         mTimerModel?.updateTimer(timer.setLabel(label))
579     }
580 
581     /**
582      * @param timer the timer whose `length` to change
583      * @param length the new length of the timer in milliseconds
584      */
setTimerLengthnull585     fun setTimerLength(timer: Timer, length: Long) {
586         Utils.enforceMainLooper()
587         mTimerModel?.updateTimer(timer.setLength(length))
588     }
589 
590     /**
591      * @param timer the timer whose `remainingTime` to change
592      * @param remainingTime the new remaining time of the timer in milliseconds
593      */
setRemainingTimenull594     fun setRemainingTime(timer: Timer, remainingTime: Long) {
595         Utils.enforceMainLooper()
596 
597         val updated = timer.setRemainingTime(remainingTime)
598         mTimerModel?.updateTimer(updated)
599         if (timer.isRunning && timer.remainingTime <= 0) {
600             mContext?.startService(TimerService.createTimerExpiredIntent(mContext!!, updated))
601         }
602     }
603 
604     /**
605      * Updates the timer notifications to be current.
606      */
updateTimerNotificationnull607     fun updateTimerNotification() {
608         Utils.enforceMainLooper()
609         mTimerModel?.updateNotification()
610     }
611 
612     /**
613      * @return the uri of the default ringtone to play for all timers when no user selection exists
614      */
615     val defaultTimerRingtoneUri: Uri
616         get() {
617             Utils.enforceMainLooper()
618             return mTimerModel!!.defaultTimerRingtoneUri
619         }
620 
621     /**
622      * @return `true` iff the ringtone to play for all timers is the silent ringtone
623      */
624     val isTimerRingtoneSilent: Boolean
625         get() {
626             Utils.enforceMainLooper()
627             return mTimerModel!!.isTimerRingtoneSilent
628         }
629 
630     var timerRingtoneUri: Uri
631         /**
632          * @return the uri of the ringtone to play for all timers
633          */
634         get() {
635             Utils.enforceMainLooper()
636             return mTimerModel!!.timerRingtoneUri
637         }
638         /**
639          * @param uri the uri of the ringtone to play for all timers
640          */
641         set(uri) {
642             Utils.enforceMainLooper()
643             mTimerModel!!.timerRingtoneUri = uri
644         }
645 
646     /**
647      * @return the title of the ringtone that is played for all timers
648      */
649     val timerRingtoneTitle: String
650         get() {
651             Utils.enforceMainLooper()
652             return mTimerModel!!.timerRingtoneTitle
653         }
654 
655     /**
656      * @return the duration, in milliseconds, of the crescendo to apply to timer ringtone playback;
657      * `0` implies no crescendo should be applied
658      */
659     val timerCrescendoDuration: Long
660         get() {
661             Utils.enforceMainLooper()
662             return mTimerModel!!.timerCrescendoDuration
663         }
664 
665     var timerVibrate: Boolean
666         /**
667          * @return whether vibrate is enabled for all timers.
668          */
669         get() {
670             Utils.enforceMainLooper()
671             return mTimerModel!!.timerVibrate
672         }
673         /**
674          * @param enabled whether vibrate is enabled for all timers.
675          */
676         set(enabled) {
677             Utils.enforceMainLooper()
678             mTimerModel!!.timerVibrate = enabled
679         }
680 
681     //
682     // Alarms
683     //
684 
685     var defaultAlarmRingtoneUri: Uri
686         /**
687          * @return the uri of the ringtone to which all new alarms default
688          */
689         get() {
690             Utils.enforceMainLooper()
691             return mAlarmModel!!.defaultAlarmRingtoneUri
692         }
693         /**
694          * @param uri the uri of the ringtone to which future new alarms will default
695          */
696         set(uri) {
697             Utils.enforceMainLooper()
698             mAlarmModel!!.defaultAlarmRingtoneUri = uri
699         }
700 
701     /**
702      * @return the duration, in milliseconds, of the crescendo to apply to alarm ringtone playback;
703      * `0` implies no crescendo should be applied
704      */
705     val alarmCrescendoDuration: Long
706         get() {
707             Utils.enforceMainLooper()
708             return mAlarmModel!!.alarmCrescendoDuration
709         }
710 
711     /**
712      * @return the behavior to execute when volume buttons are pressed while firing an alarm
713      */
714     val alarmVolumeButtonBehavior: AlarmVolumeButtonBehavior
715         get() {
716             Utils.enforceMainLooper()
717             return mAlarmModel!!.alarmVolumeButtonBehavior
718         }
719 
720     /**
721      * @return the number of minutes an alarm may ring before it has timed out and becomes missed
722      */
723     val alarmTimeout: Int
724         get() = mAlarmModel!!.alarmTimeout
725 
726     /**
727      * @return the number of minutes an alarm will remain snoozed before it rings again
728      */
729     val snoozeLength: Int
730         get() = mAlarmModel!!.snoozeLength
731 
732     //
733     // Stopwatch
734     //
735 
736     /**
737      * @param stopwatchListener to be notified when stopwatch changes or laps are added
738      */
addStopwatchListenernull739     fun addStopwatchListener(stopwatchListener: StopwatchListener) {
740         Utils.enforceMainLooper()
741         mStopwatchModel?.addStopwatchListener(stopwatchListener)
742     }
743 
744     /**
745      * @param stopwatchListener to no longer be notified when stopwatch changes or laps are added
746      */
removeStopwatchListenernull747     fun removeStopwatchListener(stopwatchListener: StopwatchListener) {
748         Utils.enforceMainLooper()
749         mStopwatchModel?.removeStopwatchListener(stopwatchListener)
750     }
751 
752     /**
753      * @return the current state of the stopwatch
754      */
755     val stopwatch: Stopwatch
756         get() {
757             Utils.enforceMainLooper()
758             return mStopwatchModel!!.stopwatch
759         }
760 
761     /**
762      * @return the stopwatch after being started
763      */
startStopwatchnull764     fun startStopwatch(): Stopwatch {
765         Utils.enforceMainLooper()
766         return mStopwatchModel!!.setStopwatch(stopwatch.start())
767     }
768 
769     /**
770      * @return the stopwatch after being paused
771      */
pauseStopwatchnull772     fun pauseStopwatch(): Stopwatch {
773         Utils.enforceMainLooper()
774         return mStopwatchModel!!.setStopwatch(stopwatch.pause())
775     }
776 
777     /**
778      * @return the stopwatch after being reset
779      */
resetStopwatchnull780     fun resetStopwatch(): Stopwatch {
781         Utils.enforceMainLooper()
782         return mStopwatchModel!!.setStopwatch(stopwatch.reset())
783     }
784 
785     /**
786      * @return the laps recorded for this stopwatch
787      */
788     val laps: List<Lap>
789         get() {
790             Utils.enforceMainLooper()
791             return mStopwatchModel!!.laps
792         }
793 
794     /**
795      * @return a newly recorded lap completed now; `null` if no more laps can be added
796      */
addLapnull797     fun addLap(): Lap? {
798         Utils.enforceMainLooper()
799         return mStopwatchModel!!.addLap()
800     }
801 
802     /**
803      * @return `true` iff more laps can be recorded
804      */
canAddMoreLapsnull805     fun canAddMoreLaps(): Boolean {
806         Utils.enforceMainLooper()
807         return mStopwatchModel!!.canAddMoreLaps()
808     }
809 
810     /**
811      * @return the longest lap time of all recorded laps and the current lap
812      */
813     val longestLapTime: Long
814         get() {
815             Utils.enforceMainLooper()
816             return mStopwatchModel!!.longestLapTime
817         }
818 
819     /**
820      * @param time a point in time after the end of the last lap
821      * @return the elapsed time between the given `time` and the end of the previous lap
822      */
getCurrentLapTimenull823     fun getCurrentLapTime(time: Long): Long {
824         Utils.enforceMainLooper()
825         return mStopwatchModel!!.getCurrentLapTime(time)
826     }
827 
828     //
829     // Time
830     // (Time settings/values are accessible from any Thread so no Thread-enforcement exists.)
831     //
832 
833     /**
834      * @return the current time in milliseconds
835      */
currentTimeMillisnull836     fun currentTimeMillis(): Long {
837         return mTimeModel!!.currentTimeMillis()
838     }
839 
840     /**
841      * @return milliseconds since boot, including time spent in sleep
842      */
elapsedRealtimenull843     fun elapsedRealtime(): Long {
844         return mTimeModel!!.elapsedRealtime()
845     }
846 
847     /**
848      * @return `true` if 24 hour time format is selected; `false` otherwise
849      */
is24HourFormatnull850     fun is24HourFormat(): Boolean {
851         return mTimeModel!!.is24HourFormat()
852     }
853 
854     /**
855      * @return a new calendar object initialized to the [.currentTimeMillis]
856      */
857     val calendar: Calendar
858         get() = mTimeModel!!.calendar
859 
860     //
861     // Ringtones
862     //
863 
864     /**
865      * Ringtone titles are cached because loading them is expensive. This method
866      * **must** be called on a background thread and is responsible for priming the
867      * cache of ringtone titles to avoid later fetching titles on the main thread.
868      */
loadRingtoneTitlesnull869     fun loadRingtoneTitles() {
870         Utils.enforceNotMainLooper()
871         mRingtoneModel?.loadRingtoneTitles()
872     }
873 
874     /**
875      * Recheck the permission to read each custom ringtone.
876      */
loadRingtonePermissionsnull877     fun loadRingtonePermissions() {
878         Utils.enforceNotMainLooper()
879         mRingtoneModel?.loadRingtonePermissions()
880     }
881 
882     /**
883      * @param uri the uri of a ringtone
884      * @return the title of the ringtone with the `uri`; `null` if it cannot be fetched
885      */
getRingtoneTitlenull886     fun getRingtoneTitle(uri: Uri): String? {
887         Utils.enforceMainLooper()
888         return mRingtoneModel?.getRingtoneTitle(uri)
889     }
890 
891     /**
892      * @param uri the uri of an audio file to use as a ringtone
893      * @param title the title of the audio content at the given `uri`
894      * @return the ringtone instance created for the audio file
895      */
addCustomRingtonenull896     fun addCustomRingtone(uri: Uri, title: String?): CustomRingtone? {
897         Utils.enforceMainLooper()
898         return mRingtoneModel?.addCustomRingtone(uri, title)
899     }
900 
901     /**
902      * @param uri identifies the ringtone to remove
903      */
removeCustomRingtonenull904     fun removeCustomRingtone(uri: Uri) {
905         Utils.enforceMainLooper()
906         mRingtoneModel?.removeCustomRingtone(uri)
907     }
908 
909     /**
910      * @return all available custom ringtones
911      */
912     val customRingtones: List<CustomRingtone>
913         get() {
914             Utils.enforceMainLooper()
915             return mRingtoneModel!!.customRingtones
916         }
917 
918     //
919     // Widgets
920     //
921 
922     /**
923      * @param widgetClass indicates the type of widget being counted
924      * @param count the number of widgets of the given type
925      * @param eventCategoryId identifies the category of event to send
926      */
updateWidgetCountnull927     fun updateWidgetCount(widgetClass: Class<*>?, count: Int, @StringRes eventCategoryId: Int) {
928         Utils.enforceMainLooper()
929         mWidgetModel!!.updateWidgetCount(widgetClass!!, count, eventCategoryId)
930     }
931 
932     //
933     // Settings
934     //
935 
936     /**
937      * @param silentSettingsListener to be notified when alarm-silencing settings change
938      */
addSilentSettingsListenernull939     fun addSilentSettingsListener(silentSettingsListener: OnSilentSettingsListener) {
940         Utils.enforceMainLooper()
941         mSilentSettingsModel?.addSilentSettingsListener(silentSettingsListener)
942     }
943 
944     /**
945      * @param silentSettingsListener to no longer be notified when alarm-silencing settings change
946      */
removeSilentSettingsListenernull947     fun removeSilentSettingsListener(silentSettingsListener: OnSilentSettingsListener) {
948         Utils.enforceMainLooper()
949         mSilentSettingsModel?.removeSilentSettingsListener(silentSettingsListener)
950     }
951 
952     /**
953      * @return the id used to discriminate relevant AlarmManager callbacks from defunct ones
954      */
955     val globalIntentId: Int
956         get() = mSettingsModel!!.globalIntentId
957 
958     /**
959      * Update the id used to discriminate relevant AlarmManager callbacks from defunct ones
960      */
updateGlobalIntentIdnull961     fun updateGlobalIntentId() {
962         Utils.enforceMainLooper()
963         mSettingsModel!!.updateGlobalIntentId()
964     }
965 
966     /**
967      * @return the style of clock to display in the clock application
968      */
969     val clockStyle: ClockStyle
970         get() {
971             Utils.enforceMainLooper()
972             return mSettingsModel!!.clockStyle
973         }
974 
975     var displayClockSeconds: Boolean
976         /**
977          * @return the style of clock to display in the clock application
978          */
979         get() {
980             Utils.enforceMainLooper()
981             return mSettingsModel!!.displayClockSeconds
982         }
983         /**
984          * @param displaySeconds whether or not to display seconds for main clock
985          */
986         set(displaySeconds) {
987             Utils.enforceMainLooper()
988             mSettingsModel!!.displayClockSeconds = displaySeconds
989         }
990 
991     /**
992      * @return the style of clock to display in the clock screensaver
993      */
994     val screensaverClockStyle: ClockStyle
995         get() {
996             Utils.enforceMainLooper()
997             return mSettingsModel!!.screensaverClockStyle
998         }
999 
1000     /**
1001      * @return `true` if the screen saver should be dimmed for lower contrast at night
1002      */
1003     val screensaverNightModeOn: Boolean
1004         get() {
1005             Utils.enforceMainLooper()
1006             return mSettingsModel!!.screensaverNightModeOn
1007         }
1008 
1009     /**
1010      * @return `true` if the users wants to automatically show a clock for their home timezone
1011      * when they have travelled outside of that timezone
1012      */
1013     val showHomeClock: Boolean
1014         get() {
1015             Utils.enforceMainLooper()
1016             return mSettingsModel!!.showHomeClock
1017         }
1018 
1019     /**
1020      * @return the display order of the weekdays, which can start with [Calendar.SATURDAY],
1021      * [Calendar.SUNDAY] or [Calendar.MONDAY]
1022      */
1023     val weekdayOrder: Weekdays.Order
1024         get() {
1025             Utils.enforceMainLooper()
1026             return mSettingsModel!!.weekdayOrder
1027         }
1028 
1029     var isRestoreBackupFinished: Boolean
1030         /**
1031          * @return `true` if the restore process (of backup and restore) has completed
1032          */
1033         get() = mSettingsModel!!.isRestoreBackupFinished
1034         /**
1035          * @param finished `true` means the restore process (of backup and restore) has completed
1036          */
1037         set(finished) {
1038             mSettingsModel!!.isRestoreBackupFinished = finished
1039         }
1040 
1041     /**
1042      * @return a description of the time zones available for selection
1043      */
1044     val timeZones: TimeZones
1045         get() {
1046             Utils.enforceMainLooper()
1047             return mSettingsModel!!.timeZones
1048         }
1049 
1050     /**
1051      * Used to execute a delegate runnable and track its completion.
1052      */
1053     private class ExecutedRunnable(private val mDelegate: Runnable) : Runnable, java.lang.Object() {
1054         var isExecuted = false
runnull1055         override fun run() {
1056             mDelegate.run()
1057             synchronized(this) {
1058                 isExecuted = true
1059                 notifyAll()
1060             }
1061         }
1062     }
1063 
1064     companion object {
1065         const val ACTION_WORLD_CITIES_CHANGED = "com.android.deskclock.WORLD_CITIES_CHANGED"
1066 
1067         /** The single instance of this data model that exists for the life of the application.  */
1068         val sDataModel = DataModel()
1069 
1070         @get:JvmStatic
1071         @get:Keep
1072         val dataModel
1073             get() = sDataModel
1074     }
1075 }