• 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.timer
18 
19 import android.app.Service
20 import android.content.Context
21 import android.content.Intent
22 import android.os.IBinder
23 
24 import com.android.deskclock.DeskClock
25 import com.android.deskclock.R
26 import com.android.deskclock.data.DataModel
27 import com.android.deskclock.data.Timer
28 import com.android.deskclock.events.Events
29 import com.android.deskclock.uidata.UiDataModel
30 
31 /**
32  *
33  * This service exists solely to allow [android.app.AlarmManager] and timer notifications
34  * to alter the state of timers without disturbing the notification shade. If an activity were used
35  * instead (even one that is not displayed) the notification manager implicitly closes the
36  * notification shade which clashes with the use case of starting/pausing/resetting timers without
37  * disturbing the notification shade.
38  *
39  * The service has a second benefit. It is used to start heads-up notifications for expired
40  * timers in the foreground. This keeps the entire application in the foreground and thus prevents
41  * the operating system from killing it while expired timers are firing.
42  */
43 class TimerService : Service() {
onBindnull44     override fun onBind(intent: Intent): IBinder? {
45         return null
46     }
47 
onStartCommandnull48     override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
49         try {
50             val action = intent.action
51             val label = intent.getIntExtra(Events.EXTRA_EVENT_LABEL, R.string.label_intent)
52             when (action) {
53                 ACTION_UPDATE_NOTIFICATION -> {
54                     DataModel.dataModel.updateTimerNotification()
55                     return START_NOT_STICKY
56                 }
57                 ACTION_RESET_EXPIRED_TIMERS -> {
58                     DataModel.dataModel.resetOrDeleteExpiredTimers(label)
59                     return START_NOT_STICKY
60                 }
61                 ACTION_RESET_UNEXPIRED_TIMERS -> {
62                     DataModel.dataModel.resetUnexpiredTimers(label)
63                     return START_NOT_STICKY
64                 }
65                 ACTION_RESET_MISSED_TIMERS -> {
66                     DataModel.dataModel.resetMissedTimers(label)
67                     return START_NOT_STICKY
68                 }
69             }
70 
71             // Look up the timer in question.
72             val timerId = intent.getIntExtra(EXTRA_TIMER_ID, -1)
73             // If the timer cannot be located, ignore the action.
74             val timer: Timer = DataModel.dataModel.getTimer(timerId) ?: return START_NOT_STICKY
75 
76             when (action) {
77                 ACTION_SHOW_TIMER -> {
78                     Events.sendTimerEvent(R.string.action_show, label)
79 
80                     // Change to the timers tab.
81                     UiDataModel.uiDataModel.selectedTab = UiDataModel.Tab.TIMERS
82 
83                     // Open DeskClock which is now positioned on the timers tab and show the timer
84                     // in question.
85                     val showTimers = Intent(this, DeskClock::class.java)
86                             .putExtra(EXTRA_TIMER_ID, timerId)
87                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
88                     startActivity(showTimers)
89                 }
90                 ACTION_START_TIMER -> {
91                     Events.sendTimerEvent(R.string.action_start, label)
92                     DataModel.dataModel.startTimer(this, timer)
93                 }
94                 ACTION_PAUSE_TIMER -> {
95                     Events.sendTimerEvent(R.string.action_pause, label)
96                     DataModel.dataModel.pauseTimer(timer)
97                 }
98                 ACTION_ADD_MINUTE_TIMER -> {
99                     Events.sendTimerEvent(R.string.action_add_minute, label)
100                     DataModel.dataModel.addTimerMinute(timer)
101                 }
102                 ACTION_RESET_TIMER -> {
103                     DataModel.dataModel.resetOrDeleteTimer(timer, label)
104                 }
105                 ACTION_TIMER_EXPIRED -> {
106                     Events.sendTimerEvent(R.string.action_fire, label)
107                     DataModel.dataModel.expireTimer(this, timer)
108                 }
109             }
110         } finally {
111             // This service is foreground when expired timers exist and stopped when none exist.
112             if (DataModel.dataModel.expiredTimers.isEmpty()) {
113                 stopSelf()
114             }
115         }
116 
117         return START_NOT_STICKY
118     }
119 
120     companion object {
121         private const val ACTION_PREFIX = "com.android.deskclock.action."
122 
123         /** Shows the tab with timers; scrolls to a specific timer.  */
124         const val ACTION_SHOW_TIMER = ACTION_PREFIX + "SHOW_TIMER"
125         /** Pauses running timers; resets expired timers.  */
126         const val ACTION_PAUSE_TIMER = ACTION_PREFIX + "PAUSE_TIMER"
127         /** Starts the sole timer.  */
128         const val ACTION_START_TIMER = ACTION_PREFIX + "START_TIMER"
129         /** Resets the timer.  */
130         const val ACTION_RESET_TIMER = ACTION_PREFIX + "RESET_TIMER"
131         /** Adds an extra minute to the timer.  */
132         const val ACTION_ADD_MINUTE_TIMER = ACTION_PREFIX + "ADD_MINUTE_TIMER"
133         /** Extra for many actions specific to a given timer.  */
134         const val EXTRA_TIMER_ID = "com.android.deskclock.extra.TIMER_ID"
135 
136         private const val ACTION_TIMER_EXPIRED = ACTION_PREFIX + "TIMER_EXPIRED"
137         private const val ACTION_UPDATE_NOTIFICATION = ACTION_PREFIX + "UPDATE_NOTIFICATION"
138         private const val ACTION_RESET_EXPIRED_TIMERS = ACTION_PREFIX + "RESET_EXPIRED_TIMERS"
139         private const val ACTION_RESET_UNEXPIRED_TIMERS = ACTION_PREFIX + "RESET_UNEXPIRED_TIMERS"
140         private const val ACTION_RESET_MISSED_TIMERS = ACTION_PREFIX + "RESET_MISSED_TIMERS"
141 
142         @JvmStatic
createTimerExpiredIntentnull143         fun createTimerExpiredIntent(context: Context, timer: Timer?): Intent {
144             val timerId = timer?.id ?: -1
145             return Intent(context, TimerService::class.java)
146                     .setAction(ACTION_TIMER_EXPIRED)
147                     .putExtra(EXTRA_TIMER_ID, timerId)
148         }
149 
createResetExpiredTimersIntentnull150         fun createResetExpiredTimersIntent(context: Context): Intent {
151             return Intent(context, TimerService::class.java)
152                     .setAction(ACTION_RESET_EXPIRED_TIMERS)
153         }
154 
createResetUnexpiredTimersIntentnull155         fun createResetUnexpiredTimersIntent(context: Context): Intent {
156             return Intent(context, TimerService::class.java)
157                     .setAction(ACTION_RESET_UNEXPIRED_TIMERS)
158         }
159 
createResetMissedTimersIntentnull160         fun createResetMissedTimersIntent(context: Context): Intent {
161             return Intent(context, TimerService::class.java)
162                     .setAction(ACTION_RESET_MISSED_TIMERS)
163         }
164 
createAddMinuteTimerIntentnull165         fun createAddMinuteTimerIntent(context: Context, timerId: Int): Intent {
166             return Intent(context, TimerService::class.java)
167                     .setAction(ACTION_ADD_MINUTE_TIMER)
168                     .putExtra(EXTRA_TIMER_ID, timerId)
169         }
170 
createUpdateNotificationIntentnull171         fun createUpdateNotificationIntent(context: Context): Intent {
172             return Intent(context, TimerService::class.java)
173                     .setAction(ACTION_UPDATE_NOTIFICATION)
174         }
175     }
176 }