TimerReceiver.java revision c117a1900fa194ee8822b376186b320d61c38e33
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;
21815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.app.PendingIntent;
22815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.content.BroadcastReceiver;
23815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.content.Context;
24815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.content.Intent;
25dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport android.content.SharedPreferences;
26dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport android.preference.PreferenceManager;
27b7224ca55c54d7298d50f28f031197f442318a19Annie Chinimport android.support.v4.app.NotificationCompat;
28b7224ca55c54d7298d50f28f031197f442318a19Annie Chinimport android.support.v4.app.NotificationManagerCompat;
29815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.util.Log;
30815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson
31dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport com.android.deskclock.DeskClock;
32b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzsteinimport com.android.deskclock.R;
335cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport com.android.deskclock.TimerRingService;
347cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelsonimport com.android.deskclock.Utils;
3550ab55f5778342f83d0fcb2e6b2c3e8cae91ca8aJames Lemieuximport com.android.deskclock.events.Events;
36dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
37dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport java.util.ArrayList;
38dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport java.util.Iterator;
39dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
40815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonpublic class TimerReceiver extends BroadcastReceiver {
41815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson    private static final String TAG = "TimerReceiver";
42815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson
4358c329f52d7952597ae7a4e88fc2ec9e15ddd93aRobyn Coultas    // Make this a large number to avoid the alarm ID's which seem to be 1, 2, ...
4458c329f52d7952597ae7a4e88fc2ec9e15ddd93aRobyn Coultas    // Must also be different than StopwatchService.NOTIFICATION_ID
4558c329f52d7952597ae7a4e88fc2ec9e15ddd93aRobyn Coultas    private static final int IN_USE_NOTIFICATION_ID = Integer.MAX_VALUE - 2;
46b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
47dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    ArrayList<TimerObj> mTimers;
48dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
49dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    @Override
50815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson    public void onReceive(final Context context, final Intent intent) {
514ae58071f0a48099021e5d141ed98606cfe999ccIsaac Katzenelson        if (Timers.LOGGING) {
524ae58071f0a48099021e5d141ed98606cfe999ccIsaac Katzenelson            Log.v(TAG, "Received intent " + intent.toString());
534ae58071f0a48099021e5d141ed98606cfe999ccIsaac Katzenelson        }
54b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        String actionType = intent.getAction();
55e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // This action does not need the timers data
56e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (Timers.NOTIF_IN_USE_CANCEL.equals(actionType)) {
57e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            cancelInUseNotification(context);
58e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            return;
59e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
60e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
61b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        // Get the updated timers data.
62b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (mTimers == null) {
6350ab55f5778342f83d0fcb2e6b2c3e8cae91ca8aJames Lemieux            mTimers = new ArrayList<>();
64b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
65b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
66b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        TimerObj.getTimersFromSharedPrefs(prefs, mTimers);
67815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson
68e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // These actions do not provide a timer ID, but do use the timers data
69e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (Timers.NOTIF_IN_USE_SHOW.equals(actionType)) {
70b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            showInUseNotification(context);
71b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return;
72e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        } else if (Timers.NOTIF_TIMES_UP_SHOW.equals(actionType)) {
73e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            showTimesUpNotification(context);
74b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return;
75e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        } else if (Timers.NOTIF_TIMES_UP_CANCEL.equals(actionType)) {
76e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            cancelTimesUpNotification(context);
77b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return;
78b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
79b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
80e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Remaining actions provide a timer Id
81e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (!intent.hasExtra(Timers.TIMER_INTENT_EXTRA)) {
82e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // No data to work with, do nothing
834ae58071f0a48099021e5d141ed98606cfe999ccIsaac Katzenelson            Log.e(TAG, "got intent without Timer data");
84e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            return;
85e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
86815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson
87e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Get the timer out of the Intent
88e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        int timerId = intent.getIntExtra(Timers.TIMER_INTENT_EXTRA, -1);
89e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (timerId == -1) {
90e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            Log.d(TAG, "OnReceive:intent without Timer data for " + actionType);
91b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
924ae58071f0a48099021e5d141ed98606cfe999ccIsaac Katzenelson
93e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        TimerObj t = Timers.findTimer(mTimers, timerId);
94b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
95b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (Timers.TIMES_UP.equals(actionType)) {
96dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            // Find the timer (if it doesn't exists, it was probably deleted).
97dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            if (t == null) {
98dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson                Log.d(TAG, " timer not found in list - do nothing");
99dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson                return;
100dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            }
101dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
102bae6862c87f851be4bbcbab57ac71ac5385c9850James Lemieux            t.setState(TimerObj.STATE_TIMESUP);
103dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            t.writeToSharedPref(prefs);
10450ab55f5778342f83d0fcb2e6b2c3e8cae91ca8aJames Lemieux            Events.sendEvent(R.string.category_timer, R.string.action_fire, 0);
10550ab55f5778342f83d0fcb2e6b2c3e8cae91ca8aJames Lemieux
106733afe52e37148f2f1d36436c1a1eadcad5b471bRobyn Coultas            // Play ringtone by using TimerRingService service with a default alarm.
107dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            Log.d(TAG, "playing ringtone");
108dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            Intent si = new Intent();
1095cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson            si.setClass(context, TimerRingService.class);
110dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            context.startService(si);
111dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
11298b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            // Update the in-use notification
1137cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelson            if (getNextRunningTimer(mTimers, false, Utils.getTimeNow()) == null) {
114b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                // Found no running timers.
115b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                cancelInUseNotification(context);
11698b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            } else {
11798b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas                showInUseNotification(context);
118dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            }
119b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
12098b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            // Start the TimerAlertFullScreen activity.
12198b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            Intent timersAlert = new Intent(context, TimerAlertFullScreen.class);
12298b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            timersAlert.setFlags(
12398b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
12498b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            context.startActivity(timersAlert);
1255f594531901d5f562c0c6a8e1d4ffd2e57709764Daria Evdokimova        } else if (Timers.RESET_TIMER.equals(actionType)
126b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                || Timers.DELETE_TIMER.equals(actionType)
127b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                || Timers.TIMER_DONE.equals(actionType)) {
12898b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas            // Stop Ringtone if all timers are not in times-up status
129b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            stopRingtoneIfNoTimesup(context);
130e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        } else if (Timers.NOTIF_TIMES_UP_STOP.equals(actionType)) {
131e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Find the timer (if it doesn't exists, it was probably deleted).
132e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (t == null) {
133e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "timer to stop not found in list - do nothing");
134e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                return;
135e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            } else if (t.mState != TimerObj.STATE_TIMESUP) {
136e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "action to stop but timer not in times-up state - do nothing");
137e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                return;
138e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
139e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
140e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Update timer state
141bae6862c87f851be4bbcbab57ac71ac5385c9850James Lemieux            t.setState(t.getDeleteAfterUse() ? TimerObj.STATE_DELETED : TimerObj.STATE_RESTART);
142d50ff479c37e33de776c339bc93df308f4ac7ab0rachelzhang            t.mTimeLeft = t.mOriginalLength = t.mSetupLength;
143e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            t.writeToSharedPref(prefs);
144e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
145e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Flag to tell DeskClock to re-sync with the database
146bae6862c87f851be4bbcbab57ac71ac5385c9850James Lemieux            prefs.edit().putBoolean(Timers.REFRESH_UI_WITH_LATEST_DATA, true).apply();
147e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
148e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            cancelTimesUpNotification(context, t);
149e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
150e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Done with timer - delete from data base
151e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (t.getDeleteAfterUse()) {
152e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                t.deleteFromSharedPref(prefs);
153e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
154e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
155e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Stop Ringtone if no timers are in times-up status
156e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            stopRingtoneIfNoTimesup(context);
157e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        } else if (Timers.NOTIF_TIMES_UP_PLUS_ONE.equals(actionType)) {
158e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Find the timer (if it doesn't exists, it was probably deleted).
159e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (t == null) {
160e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "timer to +1m not found in list - do nothing");
161e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                return;
162e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            } else if (t.mState != TimerObj.STATE_TIMESUP) {
163e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "action to +1m but timer not in times up state - do nothing");
164e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                return;
165e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
166e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
167e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Restarting the timer with 1 minute left.
168bae6862c87f851be4bbcbab57ac71ac5385c9850James Lemieux            t.setState(TimerObj.STATE_RUNNING);
169e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            t.mStartTime = Utils.getTimeNow();
170e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            t.mTimeLeft = t. mOriginalLength = TimerObj.MINUTE_IN_MILLIS;
171e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            t.writeToSharedPref(prefs);
172e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
173e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Flag to tell DeskClock to re-sync with the database
174bae6862c87f851be4bbcbab57ac71ac5385c9850James Lemieux            prefs.edit().putBoolean(Timers.REFRESH_UI_WITH_LATEST_DATA, true).apply();
175e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
176e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            cancelTimesUpNotification(context, t);
177e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
178e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // If the app is not open, refresh the in-use notification
179e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (!prefs.getBoolean(Timers.NOTIF_APP_OPEN, false)) {
180e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                showInUseNotification(context);
181e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
182e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
183e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Stop Ringtone if no timers are in times-up status
184e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            stopRingtoneIfNoTimesup(context);
185e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        } else if (Timers.TIMER_UPDATE.equals(actionType)) {
1863c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro            // Find the timer (if it doesn't exists, it was probably deleted).
1873c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro            if (t == null) {
1883c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro                Log.d(TAG, " timer to update not found in list - do nothing");
1893c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro                return;
1903c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro            }
1913c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro
192e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Refresh buzzing notification
193e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (t.mState == TimerObj.STATE_TIMESUP) {
194e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                // Must cancel the previous notification to get all updates displayed correctly
195e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                cancelTimesUpNotification(context, t);
196e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                showTimesUpNotification(context, t);
197e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
198dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        }
199dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        // Update the next "Times up" alarm
200b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        updateNextTimesup(context);
201b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
202b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
203b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private void stopRingtoneIfNoTimesup(final Context context) {
204b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (Timers.findExpiredTimer(mTimers) == null) {
205b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            // Stop ringtone
206b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            Log.d(TAG, "stopping ringtone");
207b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            Intent si = new Intent();
208b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            si.setClass(context, TimerRingService.class);
209b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            context.stopService(si);
210b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
211815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson    }
212dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
213dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    // Scan all timers and find the one that will expire next.
214dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    // Tell AlarmManager to send a "Time's up" message to this receiver when this timer expires.
215dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    // If no timer exists, clear "time's up" message.
216dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    private void updateNextTimesup(Context context) {
2177cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelson        TimerObj t = getNextRunningTimer(mTimers, false, Utils.getTimeNow());
218b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        long nextTimesup = (t == null) ? -1 : t.getTimesupTime();
219b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        int timerId = (t == null) ? -1 : t.mTimerId;
220dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
221dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        Intent intent = new Intent();
222dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        intent.setAction(Timers.TIMES_UP);
223dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        intent.setClass(context, TimerReceiver.class);
22450733f22a2989a9e3df15ecbdb1ef2ea49bbce01Budi Kusmiantoro        // Time-critical, should be foreground
22550733f22a2989a9e3df15ecbdb1ef2ea49bbce01Budi Kusmiantoro        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
226b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (!mTimers.isEmpty()) {
227b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            intent.putExtra(Timers.TIMER_INTENT_EXTRA, timerId);
228dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        }
229dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        AlarmManager mngr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
230dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        PendingIntent p = PendingIntent.getBroadcast(context,
231dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson                0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
232b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (t != null) {
233f1ce98a6e2972854968488d2a28adbc29364b0f9Isaac Katzenelson            if (Utils.isKitKatOrLater()) {
2344fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas                mngr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTimesup, p);
2354fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas            } else {
2364fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas                mngr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTimesup, p);
2374fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas            }
238e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (Timers.LOGGING) {
239e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "Setting times up to " + nextTimesup);
240e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
241dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        } else {
242dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            mngr.cancel(p);
243e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (Timers.LOGGING) {
244e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.v(TAG, "no next times up");
245e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
246dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        }
247dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    }
248dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
249b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private void showInUseNotification(final Context context) {
250b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
251b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        boolean appOpen = prefs.getBoolean(Timers.NOTIF_APP_OPEN, false);
252b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        ArrayList<TimerObj> timersInUse = Timers.timersInUse(mTimers);
253b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        int numTimersInUse = timersInUse.size();
254b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
255b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (appOpen || numTimersInUse == 0) {
256b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return;
257b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
258b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
259b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        String title, contentText;
260b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        Long nextBroadcastTime = null;
2617cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelson        long now = Utils.getTimeNow();
262b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (timersInUse.size() == 1) {
263b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            TimerObj timer = timersInUse.get(0);
264d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein            boolean timerIsTicking = timer.isTicking();
265e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            String label = timer.getLabelOrDefault(context);
266d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein            title = timerIsTicking ? label : context.getString(R.string.timer_stopped);
267d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein            long timeLeft = timerIsTicking ? timer.getTimesupTime() - now : timer.mTimeLeft;
268b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            contentText = buildTimeRemaining(context, timeLeft);
26974be96baee671f1f62b4ed543ba793336012ef24Isaac Katzenelson            if (timerIsTicking && timeLeft > TimerObj.MINUTE_IN_MILLIS) {
270b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                nextBroadcastTime = getBroadcastTime(now, timeLeft);
271b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            }
272b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        } else {
273b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            TimerObj timer = getNextRunningTimer(timersInUse, false, now);
274b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            if (timer == null) {
275b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                // No running timers.
276b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                title = String.format(
277b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        context.getString(R.string.timers_stopped), numTimersInUse);
278b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                contentText = context.getString(R.string.all_timers_stopped_notif);
279b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            } else {
280b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                // We have at least one timer running and other timers stopped.
281b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                title = String.format(
282b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        context.getString(R.string.timers_in_use), numTimersInUse);
283b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                long completionTime = timer.getTimesupTime();
284b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                long timeLeft = completionTime - now;
285b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                contentText = String.format(context.getString(R.string.next_timer_notif),
286b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        buildTimeRemaining(context, timeLeft));
28774be96baee671f1f62b4ed543ba793336012ef24Isaac Katzenelson                if (timeLeft <= TimerObj.MINUTE_IN_MILLIS) {
288b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    TimerObj timerWithUpdate = getNextRunningTimer(timersInUse, true, now);
289b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    if (timerWithUpdate != null) {
290b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        completionTime = timerWithUpdate.getTimesupTime();
291b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        timeLeft = completionTime - now;
292b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        nextBroadcastTime = getBroadcastTime(now, timeLeft);
293b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    }
294b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                } else {
295b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    nextBroadcastTime = getBroadcastTime(now, timeLeft);
296b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                }
297b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            }
298b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
299b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        showCollapsedNotificationWithNext(context, title, contentText, nextBroadcastTime);
300b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
301b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
302b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private long getBroadcastTime(long now, long timeUntilBroadcast) {
303b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        long seconds = timeUntilBroadcast / 1000;
304b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        seconds = seconds - ( (seconds / 60) * 60 );
305b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        return now + (seconds * 1000);
306b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
307b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
308b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private void showCollapsedNotificationWithNext(
309b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            final Context context, String title, String text, Long nextBroadcastTime) {
310b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        Intent activityIntent = new Intent(context, DeskClock.class);
311b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
312b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        activityIntent.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX);
313b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        PendingIntent pendingActivityIntent = PendingIntent.getActivity(context, 0, activityIntent,
314b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
315b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        showCollapsedNotification(context, title, text, NotificationCompat.PRIORITY_HIGH,
316184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein                pendingActivityIntent, IN_USE_NOTIFICATION_ID, false);
317b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
318b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (nextBroadcastTime == null) {
319b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return;
320b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
321b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        Intent nextBroadcast = new Intent();
322b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        nextBroadcast.setAction(Timers.NOTIF_IN_USE_SHOW);
323b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        PendingIntent pendingNextBroadcast =
324b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                PendingIntent.getBroadcast(context, 0, nextBroadcast, 0);
325b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        AlarmManager alarmManager =
326b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
327f1ce98a6e2972854968488d2a28adbc29364b0f9Isaac Katzenelson        if (Utils.isKitKatOrLater()) {
3284fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas            alarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextBroadcastTime, pendingNextBroadcast);
3294fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas        } else {
3304fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas            alarmManager.set(AlarmManager.ELAPSED_REALTIME, nextBroadcastTime, pendingNextBroadcast);
3314fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas        }
332b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
333b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
33472332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein    private static void showCollapsedNotification(final Context context, String title, String text,
335184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein            int priority, PendingIntent pendingIntent, int notificationId, boolean showTicker) {
336b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
337692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setAutoCancel(false)
338692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setContentTitle(title)
339692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setContentText(text)
340692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setDeleteIntent(pendingIntent)
341692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setOngoing(true)
342692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setPriority(priority)
343692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setShowWhen(false)
344b7224ca55c54d7298d50f28f031197f442318a19Annie Chin                .setSmallIcon(R.drawable.stat_notify_timer)
345b7224ca55c54d7298d50f28f031197f442318a19Annie Chin                .setCategory(NotificationCompat.CATEGORY_ALARM)
346b7224ca55c54d7298d50f28f031197f442318a19Annie Chin                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
347b7224ca55c54d7298d50f28f031197f442318a19Annie Chin                .setLocalOnly(true);
348184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein        if (showTicker) {
349184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein            builder.setTicker(text);
350184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein        }
351184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein
352b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        final Notification notification = builder.build();
353b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        notification.contentIntent = pendingIntent;
354b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        NotificationManagerCompat.from(context).notify(notificationId, notification);
355b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
356b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
357b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private String buildTimeRemaining(Context context, long timeLeft) {
358b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (timeLeft < 0) {
359b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            // We should never be here...
360b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            Log.v(TAG, "Will not show notification for timer already expired.");
361b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return null;
362b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
363b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
364671e4cb3a39a6766b21280a9efd450e57026ace9Annie Chin        long seconds, minutes, hours;
365b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        seconds = timeLeft / 1000;
366b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        minutes = seconds / 60;
367b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        hours = minutes / 60;
368b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        minutes = minutes - hours * 60;
369b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (hours > 99) {
370b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            hours = 0;
371b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
372b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
373671e4cb3a39a6766b21280a9efd450e57026ace9Annie Chin        String minSeq = Utils.getNumberFormattedQuantityString(context, R.plurals.minutes,
374671e4cb3a39a6766b21280a9efd450e57026ace9Annie Chin                (int) minutes);
375671e4cb3a39a6766b21280a9efd450e57026ace9Annie Chin
376671e4cb3a39a6766b21280a9efd450e57026ace9Annie Chin        String hourSeq = Utils.getNumberFormattedQuantityString(context, R.plurals.hours,
377671e4cb3a39a6766b21280a9efd450e57026ace9Annie Chin                (int) hours);
378b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
379b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        boolean dispHour = hours > 0;
380b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        boolean dispMinute = minutes > 0;
381b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        int index = (dispHour ? 1 : 0) | (dispMinute ? 2 : 0);
382b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        String[] formats = context.getResources().getStringArray(R.array.timer_notifications);
383b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        return String.format(formats[index], hourSeq, minSeq);
384b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
385b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
386b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private TimerObj getNextRunningTimer(
387b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            ArrayList<TimerObj> timers, boolean requireNextUpdate, long now) {
388b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        long nextTimesup = Long.MAX_VALUE;
389b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        boolean nextTimerFound = false;
390b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        Iterator<TimerObj> i = timers.iterator();
391b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        TimerObj t = null;
392b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        while(i.hasNext()) {
393b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            TimerObj tmp = i.next();
394b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            if (tmp.mState == TimerObj.STATE_RUNNING) {
395b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                long timesupTime = tmp.getTimesupTime();
396b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                long timeLeft = timesupTime - now;
397b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                if (timesupTime < nextTimesup && (!requireNextUpdate || timeLeft > 60) ) {
398b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    nextTimesup = timesupTime;
399b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    nextTimerFound = true;
400b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    t = tmp;
401b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                }
402b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            }
403b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
404b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (nextTimerFound) {
405b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return t;
406b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        } else {
407b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return null;
408b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
409b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
410b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
4115f594531901d5f562c0c6a8e1d4ffd2e57709764Daria Evdokimova    public static void cancelInUseNotification(final Context context) {
412b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        NotificationManagerCompat.from(context).cancel(IN_USE_NOTIFICATION_ID);
413b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
414e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
415e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    private void showTimesUpNotification(final Context context) {
416e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        for (TimerObj timerObj : Timers.timersInTimesUp(mTimers) ) {
417e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            showTimesUpNotification(context, timerObj);
418e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
419e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
420e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
421e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    private void showTimesUpNotification(final Context context, TimerObj timerObj) {
422e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Content Intent. When clicked will show the timer full screen
423e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        PendingIntent contentIntent = PendingIntent.getActivity(context, timerObj.mTimerId,
424e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                new Intent(context, TimerAlertFullScreen.class).putExtra(
425e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        Timers.TIMER_INTENT_EXTRA, timerObj.mTimerId),
426e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                PendingIntent.FLAG_UPDATE_CURRENT);
427e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
428e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Add one minute action button
429e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        PendingIntent addOneMinuteAction = PendingIntent.getBroadcast(context, timerObj.mTimerId,
430e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                new Intent(Timers.NOTIF_TIMES_UP_PLUS_ONE)
431e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        .putExtra(Timers.TIMER_INTENT_EXTRA, timerObj.mTimerId),
432e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                PendingIntent.FLAG_UPDATE_CURRENT);
433e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
434e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Add stop/done action button
435d50ff479c37e33de776c339bc93df308f4ac7ab0rachelzhang        PendingIntent stopIntent = PendingIntent.getBroadcast(context, timerObj.mTimerId,
436e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                new Intent(Timers.NOTIF_TIMES_UP_STOP)
437e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        .putExtra(Timers.TIMER_INTENT_EXTRA, timerObj.mTimerId),
438e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                PendingIntent.FLAG_UPDATE_CURRENT);
439e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
440e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Notification creation
441b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
442e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setContentIntent(contentIntent)
443c117a1900fa194ee8822b376186b320d61c38e33Annie Chin                .addAction(R.drawable.ic_add_24dp,
444e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        context.getResources().getString(R.string.timer_plus_1_min),
445e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        addOneMinuteAction)
446e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .addAction(
447e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        timerObj.getDeleteAfterUse()
448e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                                ? android.R.drawable.ic_menu_close_clear_cancel
4497f5209b10f08a3919245552516e60b4f07982a04Annie Chin                                : R.drawable.ic_stop_24dp,
450e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        timerObj.getDeleteAfterUse()
451e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                                ? context.getResources().getString(R.string.timer_done)
452c5deecb3c25796538d762857692182bf3e5f1e21rachelzhang                                : context.getResources().getString(R.string.timer_stop),
453d50ff479c37e33de776c339bc93df308f4ac7ab0rachelzhang                        stopIntent)
454e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setContentTitle(timerObj.getLabelOrDefault(context))
455e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setContentText(context.getResources().getString(R.string.timer_times_up))
456e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setSmallIcon(R.drawable.stat_notify_timer)
457e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setOngoing(true)
458e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setAutoCancel(false)
459b7224ca55c54d7298d50f28f031197f442318a19Annie Chin                .setPriority(NotificationCompat.PRIORITY_MAX)
460b7224ca55c54d7298d50f28f031197f442318a19Annie Chin                .setDefaults(NotificationCompat.DEFAULT_LIGHTS)
46179c464e19f63a44a416eb4844702a7ba772b9072Annie Chin                .setWhen(0);
46279c464e19f63a44a416eb4844702a7ba772b9072Annie Chin
463e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Send the notification using the timer's id to identify the
464e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // correct notification
465b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        NotificationManagerCompat.from(context).notify(timerObj.mTimerId, builder.build());
466e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (Timers.LOGGING) {
467e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            Log.v(TAG, "Setting times-up notification for "
468e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                    + timerObj.getLabelOrDefault(context) + " #" + timerObj.mTimerId);
469e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
470e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
471e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
472e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    private void cancelTimesUpNotification(final Context context) {
473e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        for (TimerObj timerObj : Timers.timersInTimesUp(mTimers) ) {
474e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            cancelTimesUpNotification(context, timerObj);
475e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
476e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
477e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
478e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    private void cancelTimesUpNotification(final Context context, TimerObj timerObj) {
479b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        NotificationManagerCompat.from(context).cancel(timerObj.mTimerId);
480e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (Timers.LOGGING) {
481e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            Log.v(TAG, "Canceling times-up notification for "
482e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                    + timerObj.getLabelOrDefault(context) + " #" + timerObj.mTimerId);
483e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
484e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
48598b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas}
486