TimerReceiver.java revision c5deecb3c25796538d762857692182bf3e5f1e21
1815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson/*
2815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * Copyright (C) 2012 The Android Open Source Project
3815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson *
4815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * Licensed under the Apache License, Version 2.0 (the "License");
5815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * you may not use this file except in compliance with the License.
6815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * You may obtain a copy of the License at
7815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson *
8815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson *      http://www.apache.org/licenses/LICENSE-2.0
9815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson *
10815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * Unless required by applicable law or agreed to in writing, software
11815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * distributed under the License is distributed on an "AS IS" BASIS,
12815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * See the License for the specific language governing permissions and
14815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * limitations under the License.
15815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson */
16815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson
17815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonpackage com.android.deskclock.timer;
18815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson
19dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport android.app.AlarmManager;
20815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.app.Notification;
21b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzsteinimport android.app.NotificationManager;
22815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.app.PendingIntent;
23815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.content.BroadcastReceiver;
24815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.content.Context;
25815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.content.Intent;
26dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport android.content.SharedPreferences;
27dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport android.preference.PreferenceManager;
28815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.util.Log;
29815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson
30dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport com.android.deskclock.DeskClock;
31b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzsteinimport com.android.deskclock.R;
325cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport com.android.deskclock.TimerRingService;
337cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelsonimport com.android.deskclock.Utils;
34dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
35dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport java.util.ArrayList;
36dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport java.util.Iterator;
37dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
38815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonpublic class TimerReceiver extends BroadcastReceiver {
39815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson    private static final String TAG = "TimerReceiver";
40815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson
4158c329f52d7952597ae7a4e88fc2ec9e15ddd93aRobyn Coultas    // Make this a large number to avoid the alarm ID's which seem to be 1, 2, ...
4258c329f52d7952597ae7a4e88fc2ec9e15ddd93aRobyn Coultas    // Must also be different than StopwatchService.NOTIFICATION_ID
4358c329f52d7952597ae7a4e88fc2ec9e15ddd93aRobyn Coultas    private static final int IN_USE_NOTIFICATION_ID = Integer.MAX_VALUE - 2;
44b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
45dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    ArrayList<TimerObj> mTimers;
46dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
47dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    @Override
48815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson    public void onReceive(final Context context, final Intent intent) {
494ae58071f0a48099021e5d141ed98606cfe999ccIsaac Katzenelson        if (Timers.LOGGING) {
504ae58071f0a48099021e5d141ed98606cfe999ccIsaac Katzenelson            Log.v(TAG, "Received intent " + intent.toString());
514ae58071f0a48099021e5d141ed98606cfe999ccIsaac Katzenelson        }
52b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        String actionType = intent.getAction();
53e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // This action does not need the timers data
54e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (Timers.NOTIF_IN_USE_CANCEL.equals(actionType)) {
55e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            cancelInUseNotification(context);
56e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            return;
57e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
58e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
59b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        // Get the updated timers data.
60b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (mTimers == null) {
61b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            mTimers = new ArrayList<TimerObj> ();
62b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
63b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
64b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        TimerObj.getTimersFromSharedPrefs(prefs, mTimers);
65815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson
66e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // These actions do not provide a timer ID, but do use the timers data
67e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (Timers.NOTIF_IN_USE_SHOW.equals(actionType)) {
68b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            showInUseNotification(context);
69b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return;
70e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        } else if (Timers.NOTIF_TIMES_UP_SHOW.equals(actionType)) {
71e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            showTimesUpNotification(context);
72b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return;
73e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        } else if (Timers.NOTIF_TIMES_UP_CANCEL.equals(actionType)) {
74e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            cancelTimesUpNotification(context);
75b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return;
76b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
77b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
78e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Remaining actions provide a timer Id
79e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (!intent.hasExtra(Timers.TIMER_INTENT_EXTRA)) {
80e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // No data to work with, do nothing
814ae58071f0a48099021e5d141ed98606cfe999ccIsaac Katzenelson            Log.e(TAG, "got intent without Timer data");
82e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            return;
83e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
84815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson
85e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Get the timer out of the Intent
86e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        int timerId = intent.getIntExtra(Timers.TIMER_INTENT_EXTRA, -1);
87e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (timerId == -1) {
88e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            Log.d(TAG, "OnReceive:intent without Timer data for " + actionType);
89b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
904ae58071f0a48099021e5d141ed98606cfe999ccIsaac Katzenelson
91e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        TimerObj t = Timers.findTimer(mTimers, timerId);
92b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
93b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (Timers.TIMES_UP.equals(actionType)) {
94dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            // Find the timer (if it doesn't exists, it was probably deleted).
95dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            if (t == null) {
96dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson                Log.d(TAG, " timer not found in list - do nothing");
97dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson                return;
98dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            }
99dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
100dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            t.mState = TimerObj.STATE_TIMESUP;
101dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            t.writeToSharedPref(prefs);
102733afe52e37148f2f1d36436c1a1eadcad5b471bRobyn Coultas            // Play ringtone by using TimerRingService service with a default alarm.
103dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            Log.d(TAG, "playing ringtone");
104dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            Intent si = new Intent();
1055cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson            si.setClass(context, TimerRingService.class);
106dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            context.startService(si);
107dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
10898b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            // Update the in-use notification
1097cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelson            if (getNextRunningTimer(mTimers, false, Utils.getTimeNow()) == null) {
110b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                // Found no running timers.
111b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                cancelInUseNotification(context);
11298b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            } else {
11398b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas                showInUseNotification(context);
114dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            }
115b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
11698b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            // Start the TimerAlertFullScreen activity.
11798b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            Intent timersAlert = new Intent(context, TimerAlertFullScreen.class);
11898b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            timersAlert.setFlags(
11998b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
12098b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            context.startActivity(timersAlert);
121b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        } else if (Timers.TIMER_RESET.equals(actionType)
122b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                || Timers.DELETE_TIMER.equals(actionType)
123b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                || Timers.TIMER_DONE.equals(actionType)) {
12498b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            // Stop Ringtone if all timers are not in times-up status
125b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            stopRingtoneIfNoTimesup(context);
126e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        } else if (Timers.NOTIF_TIMES_UP_STOP.equals(actionType)) {
127e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Find the timer (if it doesn't exists, it was probably deleted).
128e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (t == null) {
129e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "timer to stop not found in list - do nothing");
130e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                return;
131e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            } else if (t.mState != TimerObj.STATE_TIMESUP) {
132e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "action to stop but timer not in times-up state - do nothing");
133e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                return;
134e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
135e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
136e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Update timer state
137d50ff479c37e33de776c339bc93df308f4ac7ab0rachelzhang            t.mState = t.getDeleteAfterUse() ? TimerObj.STATE_DELETED : TimerObj.STATE_RESTART;
138d50ff479c37e33de776c339bc93df308f4ac7ab0rachelzhang            t.mTimeLeft = t.mOriginalLength = t.mSetupLength;
139e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            t.writeToSharedPref(prefs);
140e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
141e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Flag to tell DeskClock to re-sync with the database
142e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            prefs.edit().putBoolean(Timers.FROM_NOTIFICATION, true).apply();
143e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
144e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            cancelTimesUpNotification(context, t);
145e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
146e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Done with timer - delete from data base
147e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (t.getDeleteAfterUse()) {
148e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                t.deleteFromSharedPref(prefs);
149e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
150e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
151e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Stop Ringtone if no timers are in times-up status
152e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            stopRingtoneIfNoTimesup(context);
153e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        } else if (Timers.NOTIF_TIMES_UP_PLUS_ONE.equals(actionType)) {
154e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Find the timer (if it doesn't exists, it was probably deleted).
155e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (t == null) {
156e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "timer to +1m not found in list - do nothing");
157e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                return;
158e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            } else if (t.mState != TimerObj.STATE_TIMESUP) {
159e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "action to +1m but timer not in times up state - do nothing");
160e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                return;
161e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
162e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
163e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Restarting the timer with 1 minute left.
164e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            t.mState = TimerObj.STATE_RUNNING;
165e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            t.mStartTime = Utils.getTimeNow();
166e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            t.mTimeLeft = t. mOriginalLength = TimerObj.MINUTE_IN_MILLIS;
167e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            t.writeToSharedPref(prefs);
168e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
169e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Flag to tell DeskClock to re-sync with the database
170e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            prefs.edit().putBoolean(Timers.FROM_NOTIFICATION, true).apply();
171e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
172e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            cancelTimesUpNotification(context, t);
173e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
174e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // If the app is not open, refresh the in-use notification
175e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (!prefs.getBoolean(Timers.NOTIF_APP_OPEN, false)) {
176e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                showInUseNotification(context);
177e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
178e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
179e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Stop Ringtone if no timers are in times-up status
180e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            stopRingtoneIfNoTimesup(context);
181e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        } else if (Timers.TIMER_UPDATE.equals(actionType)) {
1823c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro            // Find the timer (if it doesn't exists, it was probably deleted).
1833c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro            if (t == null) {
1843c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro                Log.d(TAG, " timer to update not found in list - do nothing");
1853c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro                return;
1863c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro            }
1873c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro
188e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Refresh buzzing notification
189e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (t.mState == TimerObj.STATE_TIMESUP) {
190e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                // Must cancel the previous notification to get all updates displayed correctly
191e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                cancelTimesUpNotification(context, t);
192e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                showTimesUpNotification(context, t);
193e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
194dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        }
195dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        // Update the next "Times up" alarm
196b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        updateNextTimesup(context);
197b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
198b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
199b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private void stopRingtoneIfNoTimesup(final Context context) {
200b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (Timers.findExpiredTimer(mTimers) == null) {
201b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            // Stop ringtone
202b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            Log.d(TAG, "stopping ringtone");
203b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            Intent si = new Intent();
204b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            si.setClass(context, TimerRingService.class);
205b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            context.stopService(si);
206b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
207815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson    }
208dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
209dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    // Scan all timers and find the one that will expire next.
210dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    // Tell AlarmManager to send a "Time's up" message to this receiver when this timer expires.
211dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    // If no timer exists, clear "time's up" message.
212dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    private void updateNextTimesup(Context context) {
2137cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelson        TimerObj t = getNextRunningTimer(mTimers, false, Utils.getTimeNow());
214b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        long nextTimesup = (t == null) ? -1 : t.getTimesupTime();
215b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        int timerId = (t == null) ? -1 : t.mTimerId;
216dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
217dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        Intent intent = new Intent();
218dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        intent.setAction(Timers.TIMES_UP);
219dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        intent.setClass(context, TimerReceiver.class);
220b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (!mTimers.isEmpty()) {
221b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            intent.putExtra(Timers.TIMER_INTENT_EXTRA, timerId);
222dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        }
223dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        AlarmManager mngr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
224dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        PendingIntent p = PendingIntent.getBroadcast(context,
225dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson                0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
226b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (t != null) {
227f1ce98a6e2972854968488d2a28adbc29364b0f9Isaac Katzenelson            if (Utils.isKitKatOrLater()) {
2284fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas                mngr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTimesup, p);
2294fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas            } else {
2304fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas                mngr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTimesup, p);
2314fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas            }
232e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (Timers.LOGGING) {
233e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "Setting times up to " + nextTimesup);
234e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
235dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        } else {
236dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            mngr.cancel(p);
237e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (Timers.LOGGING) {
238e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.v(TAG, "no next times up");
239e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
240dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        }
241dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    }
242dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
243b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private void showInUseNotification(final Context context) {
244b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
245b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        boolean appOpen = prefs.getBoolean(Timers.NOTIF_APP_OPEN, false);
246b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        ArrayList<TimerObj> timersInUse = Timers.timersInUse(mTimers);
247b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        int numTimersInUse = timersInUse.size();
248b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
249b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (appOpen || numTimersInUse == 0) {
250b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return;
251b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
252b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
253b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        String title, contentText;
254b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        Long nextBroadcastTime = null;
2557cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelson        long now = Utils.getTimeNow();
256b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (timersInUse.size() == 1) {
257b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            TimerObj timer = timersInUse.get(0);
258d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein            boolean timerIsTicking = timer.isTicking();
259e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            String label = timer.getLabelOrDefault(context);
260d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein            title = timerIsTicking ? label : context.getString(R.string.timer_stopped);
261d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein            long timeLeft = timerIsTicking ? timer.getTimesupTime() - now : timer.mTimeLeft;
262b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            contentText = buildTimeRemaining(context, timeLeft);
26374be96baee671f1f62b4ed543ba793336012ef24Isaac Katzenelson            if (timerIsTicking && timeLeft > TimerObj.MINUTE_IN_MILLIS) {
264b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                nextBroadcastTime = getBroadcastTime(now, timeLeft);
265b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            }
266b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        } else {
267b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            TimerObj timer = getNextRunningTimer(timersInUse, false, now);
268b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            if (timer == null) {
269b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                // No running timers.
270b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                title = String.format(
271b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        context.getString(R.string.timers_stopped), numTimersInUse);
272b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                contentText = context.getString(R.string.all_timers_stopped_notif);
273b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            } else {
274b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                // We have at least one timer running and other timers stopped.
275b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                title = String.format(
276b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        context.getString(R.string.timers_in_use), numTimersInUse);
277b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                long completionTime = timer.getTimesupTime();
278b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                long timeLeft = completionTime - now;
279b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                contentText = String.format(context.getString(R.string.next_timer_notif),
280b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        buildTimeRemaining(context, timeLeft));
28174be96baee671f1f62b4ed543ba793336012ef24Isaac Katzenelson                if (timeLeft <= TimerObj.MINUTE_IN_MILLIS) {
282b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    TimerObj timerWithUpdate = getNextRunningTimer(timersInUse, true, now);
283b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    if (timerWithUpdate != null) {
284b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        completionTime = timerWithUpdate.getTimesupTime();
285b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        timeLeft = completionTime - now;
286b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        nextBroadcastTime = getBroadcastTime(now, timeLeft);
287b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    }
288b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                } else {
289b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    nextBroadcastTime = getBroadcastTime(now, timeLeft);
290b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                }
291b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            }
292b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
293b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        showCollapsedNotificationWithNext(context, title, contentText, nextBroadcastTime);
294b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
295b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
296b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private long getBroadcastTime(long now, long timeUntilBroadcast) {
297b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        long seconds = timeUntilBroadcast / 1000;
298b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        seconds = seconds - ( (seconds / 60) * 60 );
299b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        return now + (seconds * 1000);
300b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
301b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
302b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private void showCollapsedNotificationWithNext(
303b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            final Context context, String title, String text, Long nextBroadcastTime) {
304b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        Intent activityIntent = new Intent(context, DeskClock.class);
305b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
306b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        activityIntent.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX);
307b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        PendingIntent pendingActivityIntent = PendingIntent.getActivity(context, 0, activityIntent,
308b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
309b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        showCollapsedNotification(context, title, text, Notification.PRIORITY_HIGH,
310184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein                pendingActivityIntent, IN_USE_NOTIFICATION_ID, false);
311b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
312b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (nextBroadcastTime == null) {
313b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return;
314b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
315b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        Intent nextBroadcast = new Intent();
316b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        nextBroadcast.setAction(Timers.NOTIF_IN_USE_SHOW);
317b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        PendingIntent pendingNextBroadcast =
318b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                PendingIntent.getBroadcast(context, 0, nextBroadcast, 0);
319b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        AlarmManager alarmManager =
320b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
321f1ce98a6e2972854968488d2a28adbc29364b0f9Isaac Katzenelson        if (Utils.isKitKatOrLater()) {
3224fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas            alarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextBroadcastTime, pendingNextBroadcast);
3234fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas        } else {
3244fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas            alarmManager.set(AlarmManager.ELAPSED_REALTIME, nextBroadcastTime, pendingNextBroadcast);
3254fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas        }
326b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
327b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
32872332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein    private static void showCollapsedNotification(final Context context, String title, String text,
329184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein            int priority, PendingIntent pendingIntent, int notificationId, boolean showTicker) {
330184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein        Notification.Builder builder = new Notification.Builder(context)
331692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setAutoCancel(false)
332692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setContentTitle(title)
333692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setContentText(text)
334692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setDeleteIntent(pendingIntent)
335692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setOngoing(true)
336692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setPriority(priority)
337692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setShowWhen(false)
338692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setSmallIcon(R.drawable.stat_notify_timer)
339692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setCategory(Notification.CATEGORY_ALARM);
340184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein        if (showTicker) {
341184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein            builder.setTicker(text);
342184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein        }
343184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein
344184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein        Notification notification = builder.build();
345b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        notification.contentIntent = pendingIntent;
346b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        NotificationManager notificationManager =
347b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
348b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        notificationManager.notify(notificationId, notification);
349b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
350b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
351b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private String buildTimeRemaining(Context context, long timeLeft) {
352b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (timeLeft < 0) {
353b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            // We should never be here...
354b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            Log.v(TAG, "Will not show notification for timer already expired.");
355b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return null;
356b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
357b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
358b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        long hundreds, seconds, minutes, hours;
359b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        seconds = timeLeft / 1000;
360b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        minutes = seconds / 60;
361b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        seconds = seconds - minutes * 60;
362b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        hours = minutes / 60;
363b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        minutes = minutes - hours * 60;
364b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (hours > 99) {
365b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            hours = 0;
366b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
367b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
368b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        String hourSeq = (hours == 0) ? "" :
369b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            ( (hours == 1) ? context.getString(R.string.hour) :
370b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                context.getString(R.string.hours, Long.toString(hours)) );
371b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        String minSeq = (minutes == 0) ? "" :
372b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            ( (minutes == 1) ? context.getString(R.string.minute) :
373b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                context.getString(R.string.minutes, Long.toString(minutes)) );
374b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
375b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        boolean dispHour = hours > 0;
376b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        boolean dispMinute = minutes > 0;
377b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        int index = (dispHour ? 1 : 0) | (dispMinute ? 2 : 0);
378b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        String[] formats = context.getResources().getStringArray(R.array.timer_notifications);
379b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        return String.format(formats[index], hourSeq, minSeq);
380b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
381b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
382b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private TimerObj getNextRunningTimer(
383b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            ArrayList<TimerObj> timers, boolean requireNextUpdate, long now) {
384b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        long nextTimesup = Long.MAX_VALUE;
385b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        boolean nextTimerFound = false;
386b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        Iterator<TimerObj> i = timers.iterator();
387b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        TimerObj t = null;
388b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        while(i.hasNext()) {
389b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            TimerObj tmp = i.next();
390b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            if (tmp.mState == TimerObj.STATE_RUNNING) {
391b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                long timesupTime = tmp.getTimesupTime();
392b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                long timeLeft = timesupTime - now;
393b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                if (timesupTime < nextTimesup && (!requireNextUpdate || timeLeft > 60) ) {
394b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    nextTimesup = timesupTime;
395b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    nextTimerFound = true;
396b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    t = tmp;
397b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                }
398b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            }
399b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
400b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (nextTimerFound) {
401b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return t;
402b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        } else {
403b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return null;
404b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
405b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
406b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
407b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private void cancelInUseNotification(final Context context) {
408b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        NotificationManager notificationManager =
409b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
410b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        notificationManager.cancel(IN_USE_NOTIFICATION_ID);
411b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
412e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
413e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    private void showTimesUpNotification(final Context context) {
414e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        for (TimerObj timerObj : Timers.timersInTimesUp(mTimers) ) {
415e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            showTimesUpNotification(context, timerObj);
416e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
417e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
418e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
419e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    private void showTimesUpNotification(final Context context, TimerObj timerObj) {
420e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Content Intent. When clicked will show the timer full screen
421e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        PendingIntent contentIntent = PendingIntent.getActivity(context, timerObj.mTimerId,
422e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                new Intent(context, TimerAlertFullScreen.class).putExtra(
423e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        Timers.TIMER_INTENT_EXTRA, timerObj.mTimerId),
424e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                PendingIntent.FLAG_UPDATE_CURRENT);
425e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
426e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Add one minute action button
427e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        PendingIntent addOneMinuteAction = PendingIntent.getBroadcast(context, timerObj.mTimerId,
428e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                new Intent(Timers.NOTIF_TIMES_UP_PLUS_ONE)
429e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        .putExtra(Timers.TIMER_INTENT_EXTRA, timerObj.mTimerId),
430e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                PendingIntent.FLAG_UPDATE_CURRENT);
431e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
432e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Add stop/done action button
433d50ff479c37e33de776c339bc93df308f4ac7ab0rachelzhang        PendingIntent stopIntent = PendingIntent.getBroadcast(context, timerObj.mTimerId,
434e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                new Intent(Timers.NOTIF_TIMES_UP_STOP)
435e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        .putExtra(Timers.TIMER_INTENT_EXTRA, timerObj.mTimerId),
436e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                PendingIntent.FLAG_UPDATE_CURRENT);
437e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
438e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Notification creation
439e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        Notification notification = new Notification.Builder(context)
440e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setContentIntent(contentIntent)
441e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .addAction(R.drawable.ic_menu_add,
442e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        context.getResources().getString(R.string.timer_plus_1_min),
443e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        addOneMinuteAction)
444e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .addAction(
445e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        timerObj.getDeleteAfterUse()
446e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                                ? android.R.drawable.ic_menu_close_clear_cancel
447c5deecb3c25796538d762857692182bf3e5f1e21rachelzhang                                : R.drawable.ic_notify_stop,
448e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        timerObj.getDeleteAfterUse()
449e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                                ? context.getResources().getString(R.string.timer_done)
450c5deecb3c25796538d762857692182bf3e5f1e21rachelzhang                                : context.getResources().getString(R.string.timer_stop),
451d50ff479c37e33de776c339bc93df308f4ac7ab0rachelzhang                        stopIntent)
452e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setContentTitle(timerObj.getLabelOrDefault(context))
453e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setContentText(context.getResources().getString(R.string.timer_times_up))
454e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setSmallIcon(R.drawable.stat_notify_timer)
455e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setOngoing(true)
456e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setAutoCancel(false)
457e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setPriority(Notification.PRIORITY_MAX)
458e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setDefaults(Notification.DEFAULT_LIGHTS)
459e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setWhen(0)
460692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setCategory(Notification.CATEGORY_ALARM)
461e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .build();
462e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
463e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Send the notification using the timer's id to identify the
464e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // correct notification
465e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).notify(
466e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                timerObj.mTimerId, notification);
467e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (Timers.LOGGING) {
468e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            Log.v(TAG, "Setting times-up notification for "
469e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                    + timerObj.getLabelOrDefault(context) + " #" + timerObj.mTimerId);
470e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
471e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
472e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
473e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    private void cancelTimesUpNotification(final Context context) {
474e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        for (TimerObj timerObj : Timers.timersInTimesUp(mTimers) ) {
475e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            cancelTimesUpNotification(context, timerObj);
476e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
477e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
478e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
479e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    private void cancelTimesUpNotification(final Context context, TimerObj timerObj) {
480e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        NotificationManager notificationManager =
481e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
482e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        notificationManager.cancel(timerObj.mTimerId);
483e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (Timers.LOGGING) {
484e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            Log.v(TAG, "Canceling times-up notification for "
485e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                    + timerObj.getLabelOrDefault(context) + " #" + timerObj.mTimerId);
486e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
487e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
48898b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas}
489