TimerService.java revision 0a3313e231702cc9944e9a17e52aea62eb25afab
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
17package com.android.deskclock.timer;
18
19import android.app.Service;
20import android.content.Context;
21import android.content.Intent;
22import android.os.IBinder;
23
24import com.android.deskclock.HandleDeskClockApiCalls;
25import com.android.deskclock.R;
26import com.android.deskclock.data.DataModel;
27import com.android.deskclock.data.Timer;
28import com.android.deskclock.events.Events;
29
30/**
31 * <p>This service exists solely to allow {@link android.app.AlarmManager} and timer notifications
32 * to alter the state of timers without disturbing the notification shade. If an activity were used
33 * instead (even one that is not displayed) the notification manager implicitly closes the
34 * notification shade which clashes with the use case of starting/pausing/resetting timers without
35 * disturbing the notification shade.</p>
36 *
37 * <p>The service has a second benefit. It is used to start heads-up notifications for expired
38 * timers in the foreground. This keeps the entire application in the foreground and thus prevents
39 * the operating system from killing it while expired timers are firing.</p>
40 */
41public final class TimerService extends Service {
42
43    private static final String ACTION_PREFIX = "com.android.deskclock.action.";
44
45    private static final String ACTION_TIMER_EXPIRED = ACTION_PREFIX + "TIMER_EXPIRED";
46    private static final String ACTION_UPDATE_NOTIFICATION = ACTION_PREFIX + "UPDATE_NOTIFICATION";
47    private static final String ACTION_UPDATE_MISSED_NOTIFICATION = ACTION_PREFIX +
48            "UPDATE_MISSED_NOTIFICATION";
49    private static final String ACTION_RESET_EXPIRED_TIMERS = ACTION_PREFIX +
50            "RESET_EXPIRED_TIMERS";
51    private static final String ACTION_RESET_UNEXPIRED_TIMERS = ACTION_PREFIX +
52            "RESET_UNEXPIRED_TIMERS";
53    private static final String ACTION_RESET_MISSED_TIMERS = ACTION_PREFIX +
54            "RESET_MISSED_TIMERS";
55
56    public static Intent createTimerExpiredIntent(Context context, Timer timer) {
57        final int timerId = timer == null ? -1 : timer.getId();
58        return new Intent(context, TimerService.class)
59                .setAction(ACTION_TIMER_EXPIRED)
60                .putExtra(HandleDeskClockApiCalls.EXTRA_TIMER_ID, timerId);
61    }
62
63    public static Intent createResetExpiredTimersIntent(Context context) {
64        return new Intent(context, TimerService.class)
65                .setAction(ACTION_RESET_EXPIRED_TIMERS);
66    }
67
68    public static Intent createResetUnexpiredTimersIntent(Context context) {
69        return new Intent(context, TimerService.class)
70                .setAction(ACTION_RESET_UNEXPIRED_TIMERS);
71    }
72
73    public static Intent createResetMissedTimersIntent(Context context) {
74        return new Intent(context, TimerService.class)
75                .setAction(ACTION_RESET_MISSED_TIMERS);
76    }
77
78
79    public static Intent createAddMinuteTimerIntent(Context context, int timerId) {
80        return new Intent(context, TimerService.class)
81                .setAction(HandleDeskClockApiCalls.ACTION_ADD_MINUTE_TIMER)
82                .putExtra(HandleDeskClockApiCalls.EXTRA_TIMER_ID, timerId);
83    }
84
85    public static Intent createUpdateNotificationIntent(Context context) {
86        return new Intent(context, TimerService.class)
87                .setAction(ACTION_UPDATE_NOTIFICATION);
88    }
89
90    public static Intent createUpdateMissedNotificationIntent(Context context) {
91        return new Intent(context, TimerService.class)
92                .setAction(ACTION_UPDATE_MISSED_NOTIFICATION);
93    }
94
95    @Override
96    public IBinder onBind(Intent intent) {
97        return null;
98    }
99
100    @Override
101    public int onStartCommand(Intent intent, int flags, int startId) {
102        try {
103            switch (intent.getAction()) {
104                case ACTION_UPDATE_NOTIFICATION: {
105                    DataModel.getDataModel().updateTimerNotification();
106                    return START_NOT_STICKY;
107                }
108                case ACTION_UPDATE_MISSED_NOTIFICATION: {
109                    DataModel.getDataModel().updateMissedTimerNotification();
110                    return START_NOT_STICKY;
111                }
112                case ACTION_RESET_EXPIRED_TIMERS: {
113                    DataModel.getDataModel().resetExpiredTimers(R.string.label_notification);
114                    return START_NOT_STICKY;
115                }
116                case ACTION_RESET_UNEXPIRED_TIMERS: {
117                    DataModel.getDataModel().resetUnexpiredTimers(R.string.label_notification);
118                    return START_NOT_STICKY;
119                }
120                case ACTION_RESET_MISSED_TIMERS: {
121                    DataModel.getDataModel().resetMissedTimers(R.string.label_notification);
122                    return START_NOT_STICKY;
123                }
124            }
125
126            // Look up the timer in question.
127            final int timerId = intent.getIntExtra(HandleDeskClockApiCalls.EXTRA_TIMER_ID, -1);
128            final Timer timer = DataModel.getDataModel().getTimer(timerId);
129
130            // If the timer cannot be located, ignore the action.
131            if (timer == null) {
132                return START_NOT_STICKY;
133            }
134
135            // Perform the action on the timer.
136            switch (intent.getAction()) {
137                case HandleDeskClockApiCalls.ACTION_START_TIMER:
138                    DataModel.getDataModel().startTimer(this, timer);
139                    Events.sendTimerEvent(R.string.action_start, R.string.label_notification);
140                    break;
141                case HandleDeskClockApiCalls.ACTION_PAUSE_TIMER:
142                    DataModel.getDataModel().pauseTimer(timer);
143                    Events.sendTimerEvent(R.string.action_pause, R.string.label_notification);
144                    break;
145                case HandleDeskClockApiCalls.ACTION_ADD_MINUTE_TIMER:
146                    DataModel.getDataModel().addTimerMinute(timer);
147                    Events.sendTimerEvent(R.string.action_add_minute, R.string.label_notification);
148                    break;
149                case HandleDeskClockApiCalls.ACTION_RESET_TIMER:
150                    DataModel.getDataModel().resetOrDeleteTimer(timer, R.string.label_notification);
151                    break;
152                case ACTION_TIMER_EXPIRED:
153                    DataModel.getDataModel().expireTimer(this, timer);
154                    Events.sendTimerEvent(R.string.action_fire, R.string.label_intent);
155                    break;
156            }
157        } finally {
158            // This service is foreground when expired timers exist and stopped when none exist.
159            if (DataModel.getDataModel().getExpiredTimers().isEmpty()) {
160                stopSelf();
161            }
162        }
163
164        return START_NOT_STICKY;
165    }
166}