• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 import static com.android.deskclock.uidata.UiDataModel.Tab.TIMERS;
32 
33 /**
34  * <p>This service exists solely to allow {@link android.app.AlarmManager} and timer notifications
35  * to alter the state of timers without disturbing the notification shade. If an activity were used
36  * instead (even one that is not displayed) the notification manager implicitly closes the
37  * notification shade which clashes with the use case of starting/pausing/resetting timers without
38  * disturbing the notification shade.</p>
39  *
40  * <p>The service has a second benefit. It is used to start heads-up notifications for expired
41  * timers in the foreground. This keeps the entire application in the foreground and thus prevents
42  * the operating system from killing it while expired timers are firing.</p>
43  */
44 public final class TimerService extends Service {
45 
46     private static final String ACTION_PREFIX = "com.android.deskclock.action.";
47 
48     /** Shows the tab with timers; scrolls to a specific timer. */
49     public static final String ACTION_SHOW_TIMER = ACTION_PREFIX + "SHOW_TIMER";
50     /** Pauses running timers; resets expired timers. */
51     public static final String ACTION_PAUSE_TIMER = ACTION_PREFIX + "PAUSE_TIMER";
52     /** Starts the sole timer. */
53     public static final String ACTION_START_TIMER = ACTION_PREFIX + "START_TIMER";
54     /** Resets the timer. */
55     public static final String ACTION_RESET_TIMER = ACTION_PREFIX + "RESET_TIMER";
56     /** Adds an extra minute to the timer. */
57     public static final String ACTION_ADD_MINUTE_TIMER = ACTION_PREFIX + "ADD_MINUTE_TIMER";
58 
59     /** Extra for many actions specific to a given timer. */
60     public static final String EXTRA_TIMER_ID = "com.android.deskclock.extra.TIMER_ID";
61 
62     private static final String ACTION_TIMER_EXPIRED =
63             ACTION_PREFIX + "TIMER_EXPIRED";
64     private static final String ACTION_UPDATE_NOTIFICATION =
65             ACTION_PREFIX + "UPDATE_NOTIFICATION";
66     private static final String ACTION_RESET_EXPIRED_TIMERS =
67             ACTION_PREFIX + "RESET_EXPIRED_TIMERS";
68     private static final String ACTION_RESET_UNEXPIRED_TIMERS =
69             ACTION_PREFIX + "RESET_UNEXPIRED_TIMERS";
70     private static final String ACTION_RESET_MISSED_TIMERS =
71             ACTION_PREFIX + "RESET_MISSED_TIMERS";
72 
createTimerExpiredIntent(Context context, Timer timer)73     public static Intent createTimerExpiredIntent(Context context, Timer timer) {
74         final int timerId = timer == null ? -1 : timer.getId();
75         return new Intent(context, TimerService.class)
76                 .setAction(ACTION_TIMER_EXPIRED)
77                 .putExtra(EXTRA_TIMER_ID, timerId);
78     }
79 
createResetExpiredTimersIntent(Context context)80     public static Intent createResetExpiredTimersIntent(Context context) {
81         return new Intent(context, TimerService.class)
82                 .setAction(ACTION_RESET_EXPIRED_TIMERS);
83     }
84 
createResetUnexpiredTimersIntent(Context context)85     public static Intent createResetUnexpiredTimersIntent(Context context) {
86         return new Intent(context, TimerService.class)
87                 .setAction(ACTION_RESET_UNEXPIRED_TIMERS);
88     }
89 
createResetMissedTimersIntent(Context context)90     public static Intent createResetMissedTimersIntent(Context context) {
91         return new Intent(context, TimerService.class)
92                 .setAction(ACTION_RESET_MISSED_TIMERS);
93     }
94 
95 
createAddMinuteTimerIntent(Context context, int timerId)96     public static Intent createAddMinuteTimerIntent(Context context, int timerId) {
97         return new Intent(context, TimerService.class)
98                 .setAction(ACTION_ADD_MINUTE_TIMER)
99                 .putExtra(EXTRA_TIMER_ID, timerId);
100     }
101 
createUpdateNotificationIntent(Context context)102     public static Intent createUpdateNotificationIntent(Context context) {
103         return new Intent(context, TimerService.class)
104                 .setAction(ACTION_UPDATE_NOTIFICATION);
105     }
106 
107     @Override
onBind(Intent intent)108     public IBinder onBind(Intent intent) {
109         return null;
110     }
111 
112     @Override
onStartCommand(Intent intent, int flags, int startId)113     public int onStartCommand(Intent intent, int flags, int startId) {
114         try {
115             final String action = intent.getAction();
116             final int label = intent.getIntExtra(Events.EXTRA_EVENT_LABEL, R.string.label_intent);
117             switch (action) {
118                 case ACTION_UPDATE_NOTIFICATION: {
119                     DataModel.getDataModel().updateTimerNotification();
120                     return START_NOT_STICKY;
121                 }
122                 case ACTION_RESET_EXPIRED_TIMERS: {
123                     DataModel.getDataModel().resetOrDeleteExpiredTimers(label);
124                     return START_NOT_STICKY;
125                 }
126                 case ACTION_RESET_UNEXPIRED_TIMERS: {
127                     DataModel.getDataModel().resetUnexpiredTimers(label);
128                     return START_NOT_STICKY;
129                 }
130                 case ACTION_RESET_MISSED_TIMERS: {
131                     DataModel.getDataModel().resetMissedTimers(label);
132                     return START_NOT_STICKY;
133                 }
134             }
135 
136             // Look up the timer in question.
137             final int timerId = intent.getIntExtra(EXTRA_TIMER_ID, -1);
138             final Timer timer = DataModel.getDataModel().getTimer(timerId);
139 
140             // If the timer cannot be located, ignore the action.
141             if (timer == null) {
142                 return START_NOT_STICKY;
143             }
144 
145             // Perform the action on the timer.
146             switch (action) {
147                 case ACTION_SHOW_TIMER: {
148                     Events.sendTimerEvent(R.string.action_show, label);
149 
150                     // Change to the timers tab.
151                     UiDataModel.getUiDataModel().setSelectedTab(TIMERS);
152 
153                     // Open DeskClock which is now positioned on the timers tab and show the timer
154                     // in question.
155                     final Intent showTimers = new Intent(this, DeskClock.class)
156                             .putExtra(EXTRA_TIMER_ID, timerId)
157                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
158                     startActivity(showTimers);
159                     break;
160                 } case ACTION_START_TIMER: {
161                     Events.sendTimerEvent(R.string.action_start, label);
162                     DataModel.getDataModel().startTimer(this, timer);
163                     break;
164                 } case ACTION_PAUSE_TIMER: {
165                     Events.sendTimerEvent(R.string.action_pause, label);
166                     DataModel.getDataModel().pauseTimer(timer);
167                     break;
168                 } case ACTION_ADD_MINUTE_TIMER: {
169                     Events.sendTimerEvent(R.string.action_add_minute, label);
170                     DataModel.getDataModel().addTimerMinute(timer);
171                     break;
172                 } case ACTION_RESET_TIMER: {
173                     DataModel.getDataModel().resetOrDeleteTimer(timer, label);
174                     break;
175                 } case ACTION_TIMER_EXPIRED: {
176                     Events.sendTimerEvent(R.string.action_fire, label);
177                     DataModel.getDataModel().expireTimer(this, timer);
178                     break;
179                 }
180             }
181         } finally {
182             // This service is foreground when expired timers exist and stopped when none exist.
183             if (DataModel.getDataModel().getExpiredTimers().isEmpty()) {
184                 stopSelf();
185             }
186         }
187 
188         return START_NOT_STICKY;
189     }
190 }
191