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);
1308d659d7d0da84881b7312e48b5ec36ecdca9a166Annie Chin
1318d659d7d0da84881b7312e48b5ec36ecdca9a166Annie Chin            if (t != null) {
1328d659d7d0da84881b7312e48b5ec36ecdca9a166Annie Chin                cancelTimesUpNotification(context, t);
1338d659d7d0da84881b7312e48b5ec36ecdca9a166Annie Chin            }
134e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        } else if (Timers.NOTIF_TIMES_UP_STOP.equals(actionType)) {
135e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Find the timer (if it doesn't exists, it was probably deleted).
136e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (t == null) {
137e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "timer to stop not found in list - do nothing");
138e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                return;
139e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            } else if (t.mState != TimerObj.STATE_TIMESUP) {
140e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "action to stop but timer not in times-up state - do nothing");
141e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                return;
142e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
143e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
144e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Update timer state
145bae6862c87f851be4bbcbab57ac71ac5385c9850James Lemieux            t.setState(t.getDeleteAfterUse() ? TimerObj.STATE_DELETED : TimerObj.STATE_RESTART);
146d50ff479c37e33de776c339bc93df308f4ac7ab0rachelzhang            t.mTimeLeft = t.mOriginalLength = t.mSetupLength;
147e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            t.writeToSharedPref(prefs);
148e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
149e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Flag to tell DeskClock to re-sync with the database
150bae6862c87f851be4bbcbab57ac71ac5385c9850James Lemieux            prefs.edit().putBoolean(Timers.REFRESH_UI_WITH_LATEST_DATA, true).apply();
151e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
152e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            cancelTimesUpNotification(context, t);
153e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
154e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Done with timer - delete from data base
155e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (t.getDeleteAfterUse()) {
156e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                t.deleteFromSharedPref(prefs);
157e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
158e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
159e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Stop Ringtone if no timers are in times-up status
160e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            stopRingtoneIfNoTimesup(context);
161e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        } else if (Timers.NOTIF_TIMES_UP_PLUS_ONE.equals(actionType)) {
162e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Find the timer (if it doesn't exists, it was probably deleted).
163e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (t == null) {
164e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "timer to +1m not found in list - do nothing");
165e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                return;
166e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            } else if (t.mState != TimerObj.STATE_TIMESUP) {
167e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "action to +1m but timer not in times up state - do nothing");
168e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                return;
169e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
170e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
171e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Restarting the timer with 1 minute left.
172bae6862c87f851be4bbcbab57ac71ac5385c9850James Lemieux            t.setState(TimerObj.STATE_RUNNING);
173e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            t.mStartTime = Utils.getTimeNow();
174e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            t.mTimeLeft = t. mOriginalLength = TimerObj.MINUTE_IN_MILLIS;
175e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            t.writeToSharedPref(prefs);
176e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
177e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Flag to tell DeskClock to re-sync with the database
178bae6862c87f851be4bbcbab57ac71ac5385c9850James Lemieux            prefs.edit().putBoolean(Timers.REFRESH_UI_WITH_LATEST_DATA, true).apply();
179e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
180e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            cancelTimesUpNotification(context, t);
181e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
182e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // If the app is not open, refresh the in-use notification
183e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (!prefs.getBoolean(Timers.NOTIF_APP_OPEN, false)) {
184e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                showInUseNotification(context);
185e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
186e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
187e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Stop Ringtone if no timers are in times-up status
188e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            stopRingtoneIfNoTimesup(context);
189e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        } else if (Timers.TIMER_UPDATE.equals(actionType)) {
1903c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro            // Find the timer (if it doesn't exists, it was probably deleted).
1913c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro            if (t == null) {
1923c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro                Log.d(TAG, " timer to update not found in list - do nothing");
1933c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro                return;
1943c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro            }
1953c16add0cffe1f7a537acca8537cdd3bf140ba97Budi Kusmiantoro
196e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            // Refresh buzzing notification
197e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (t.mState == TimerObj.STATE_TIMESUP) {
198e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                // Must cancel the previous notification to get all updates displayed correctly
199e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                cancelTimesUpNotification(context, t);
200e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                showTimesUpNotification(context, t);
201e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
202dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        }
20379a6a3c37392c7844c573999da5159653e308c5bAnnie Chin        if (intent.getBooleanExtra(Timers.UPDATE_NEXT_TIMESUP, true)) {
20479a6a3c37392c7844c573999da5159653e308c5bAnnie Chin            // Update the next "Times up" alarm unless explicitly told not to.
20579a6a3c37392c7844c573999da5159653e308c5bAnnie Chin            updateNextTimesup(context);
20679a6a3c37392c7844c573999da5159653e308c5bAnnie Chin        }
207b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
208b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
209b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private void stopRingtoneIfNoTimesup(final Context context) {
210b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (Timers.findExpiredTimer(mTimers) == null) {
211b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            // Stop ringtone
212b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            Log.d(TAG, "stopping ringtone");
213b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            Intent si = new Intent();
214b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            si.setClass(context, TimerRingService.class);
215b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            context.stopService(si);
216b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
217815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson    }
218dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
219dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    // Scan all timers and find the one that will expire next.
220dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    // Tell AlarmManager to send a "Time's up" message to this receiver when this timer expires.
221dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    // If no timer exists, clear "time's up" message.
222dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    private void updateNextTimesup(Context context) {
2237cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelson        TimerObj t = getNextRunningTimer(mTimers, false, Utils.getTimeNow());
224b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        long nextTimesup = (t == null) ? -1 : t.getTimesupTime();
225b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        int timerId = (t == null) ? -1 : t.mTimerId;
226dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
227dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        Intent intent = new Intent();
228dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        intent.setAction(Timers.TIMES_UP);
229dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        intent.setClass(context, TimerReceiver.class);
23050733f22a2989a9e3df15ecbdb1ef2ea49bbce01Budi Kusmiantoro        // Time-critical, should be foreground
23150733f22a2989a9e3df15ecbdb1ef2ea49bbce01Budi Kusmiantoro        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
232b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (!mTimers.isEmpty()) {
233b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            intent.putExtra(Timers.TIMER_INTENT_EXTRA, timerId);
234dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        }
235dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        AlarmManager mngr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
236dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        PendingIntent p = PendingIntent.getBroadcast(context,
237dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson                0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
238b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (t != null) {
239f1ce98a6e2972854968488d2a28adbc29364b0f9Isaac Katzenelson            if (Utils.isKitKatOrLater()) {
2404fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas                mngr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTimesup, p);
2414fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas            } else {
2424fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas                mngr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTimesup, p);
2434fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas            }
244e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (Timers.LOGGING) {
245e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.d(TAG, "Setting times up to " + nextTimesup);
246e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
247dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        } else {
2486b7d6b9bc2f3b5b2d744224094461cf98af10f68Daria Evdokimova            // if no timer is found Pending Intents should be canceled
2496b7d6b9bc2f3b5b2d744224094461cf98af10f68Daria Evdokimova            // to keep the internal state consistent with the UI
250dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson            mngr.cancel(p);
2516b7d6b9bc2f3b5b2d744224094461cf98af10f68Daria Evdokimova            p.cancel();
252e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            if (Timers.LOGGING) {
253e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                Log.v(TAG, "no next times up");
254e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            }
255dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson        }
256dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson    }
257dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson
258b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private void showInUseNotification(final Context context) {
259b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
260b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        boolean appOpen = prefs.getBoolean(Timers.NOTIF_APP_OPEN, false);
261b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        ArrayList<TimerObj> timersInUse = Timers.timersInUse(mTimers);
262b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        int numTimersInUse = timersInUse.size();
263b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
264b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (appOpen || numTimersInUse == 0) {
265b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return;
266b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
267b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
268b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        String title, contentText;
269b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        Long nextBroadcastTime = null;
2707cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelson        long now = Utils.getTimeNow();
271b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (timersInUse.size() == 1) {
272b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            TimerObj timer = timersInUse.get(0);
273d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein            boolean timerIsTicking = timer.isTicking();
274e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            String label = timer.getLabelOrDefault(context);
275d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein            title = timerIsTicking ? label : context.getString(R.string.timer_stopped);
276d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein            long timeLeft = timerIsTicking ? timer.getTimesupTime() - now : timer.mTimeLeft;
277b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            contentText = buildTimeRemaining(context, timeLeft);
27874be96baee671f1f62b4ed543ba793336012ef24Isaac Katzenelson            if (timerIsTicking && timeLeft > TimerObj.MINUTE_IN_MILLIS) {
279b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                nextBroadcastTime = getBroadcastTime(now, timeLeft);
280b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            }
281b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        } else {
282b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            TimerObj timer = getNextRunningTimer(timersInUse, false, now);
283b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            if (timer == null) {
284b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                // No running timers.
285b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                title = String.format(
286b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        context.getString(R.string.timers_stopped), numTimersInUse);
287b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                contentText = context.getString(R.string.all_timers_stopped_notif);
288b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            } else {
289b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                // We have at least one timer running and other timers stopped.
290b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                title = String.format(
291b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        context.getString(R.string.timers_in_use), numTimersInUse);
292b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                long completionTime = timer.getTimesupTime();
293b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                long timeLeft = completionTime - now;
294b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                contentText = String.format(context.getString(R.string.next_timer_notif),
295b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        buildTimeRemaining(context, timeLeft));
29674be96baee671f1f62b4ed543ba793336012ef24Isaac Katzenelson                if (timeLeft <= TimerObj.MINUTE_IN_MILLIS) {
297b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    TimerObj timerWithUpdate = getNextRunningTimer(timersInUse, true, now);
298b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    if (timerWithUpdate != null) {
299b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        completionTime = timerWithUpdate.getTimesupTime();
300b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        timeLeft = completionTime - now;
301b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                        nextBroadcastTime = getBroadcastTime(now, timeLeft);
302b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    }
303b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                } else {
304b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    nextBroadcastTime = getBroadcastTime(now, timeLeft);
305b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                }
306b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            }
307b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
308b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        showCollapsedNotificationWithNext(context, title, contentText, nextBroadcastTime);
309b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
310b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
311b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private long getBroadcastTime(long now, long timeUntilBroadcast) {
312b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        long seconds = timeUntilBroadcast / 1000;
313b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        seconds = seconds - ( (seconds / 60) * 60 );
314b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        return now + (seconds * 1000);
315b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
316b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
317b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private void showCollapsedNotificationWithNext(
318b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            final Context context, String title, String text, Long nextBroadcastTime) {
319b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        Intent activityIntent = new Intent(context, DeskClock.class);
320b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
321b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        activityIntent.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX);
322b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        PendingIntent pendingActivityIntent = PendingIntent.getActivity(context, 0, activityIntent,
323b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
324b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        showCollapsedNotification(context, title, text, NotificationCompat.PRIORITY_HIGH,
325184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein                pendingActivityIntent, IN_USE_NOTIFICATION_ID, false);
326b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
327b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (nextBroadcastTime == null) {
328b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return;
329b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
330b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        Intent nextBroadcast = new Intent();
331b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        nextBroadcast.setAction(Timers.NOTIF_IN_USE_SHOW);
332b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        PendingIntent pendingNextBroadcast =
333b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                PendingIntent.getBroadcast(context, 0, nextBroadcast, 0);
334b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        AlarmManager alarmManager =
335b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
336f1ce98a6e2972854968488d2a28adbc29364b0f9Isaac Katzenelson        if (Utils.isKitKatOrLater()) {
3374fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas            alarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextBroadcastTime, pendingNextBroadcast);
3384fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas        } else {
3394fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas            alarmManager.set(AlarmManager.ELAPSED_REALTIME, nextBroadcastTime, pendingNextBroadcast);
3404fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas        }
341b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
342b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
34372332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein    private static void showCollapsedNotification(final Context context, String title, String text,
344184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein            int priority, PendingIntent pendingIntent, int notificationId, boolean showTicker) {
345b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
346692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setAutoCancel(false)
347692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setContentTitle(title)
348692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setContentText(text)
349692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setDeleteIntent(pendingIntent)
350692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setOngoing(true)
351692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setPriority(priority)
352692718499fc480f29a9aabcc3cb38ba6bb7847ceBudi Kusmiantoro                .setShowWhen(false)
353b7224ca55c54d7298d50f28f031197f442318a19Annie Chin                .setSmallIcon(R.drawable.stat_notify_timer)
354b7224ca55c54d7298d50f28f031197f442318a19Annie Chin                .setCategory(NotificationCompat.CATEGORY_ALARM)
355b7224ca55c54d7298d50f28f031197f442318a19Annie Chin                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
356b7224ca55c54d7298d50f28f031197f442318a19Annie Chin                .setLocalOnly(true);
357184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein        if (showTicker) {
358184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein            builder.setTicker(text);
359184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein        }
360184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein
361b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        final Notification notification = builder.build();
362b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        notification.contentIntent = pendingIntent;
363b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        NotificationManagerCompat.from(context).notify(notificationId, notification);
364b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
365b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
366b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private String buildTimeRemaining(Context context, long timeLeft) {
367b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (timeLeft < 0) {
368b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            // We should never be here...
369b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            Log.v(TAG, "Will not show notification for timer already expired.");
370b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return null;
371b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
372b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
373671e4cb3a39a6766b21280a9efd450e57026ace9Annie Chin        long seconds, minutes, hours;
374b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        seconds = timeLeft / 1000;
375b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        minutes = seconds / 60;
376b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        hours = minutes / 60;
377b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        minutes = minutes - hours * 60;
378b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (hours > 99) {
379b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            hours = 0;
380b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
381b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
382671e4cb3a39a6766b21280a9efd450e57026ace9Annie Chin        String minSeq = Utils.getNumberFormattedQuantityString(context, R.plurals.minutes,
383671e4cb3a39a6766b21280a9efd450e57026ace9Annie Chin                (int) minutes);
384671e4cb3a39a6766b21280a9efd450e57026ace9Annie Chin
385671e4cb3a39a6766b21280a9efd450e57026ace9Annie Chin        String hourSeq = Utils.getNumberFormattedQuantityString(context, R.plurals.hours,
386671e4cb3a39a6766b21280a9efd450e57026ace9Annie Chin                (int) hours);
387b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
388b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        boolean dispHour = hours > 0;
389b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        boolean dispMinute = minutes > 0;
390b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        int index = (dispHour ? 1 : 0) | (dispMinute ? 2 : 0);
391b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        String[] formats = context.getResources().getStringArray(R.array.timer_notifications);
392b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        return String.format(formats[index], hourSeq, minSeq);
393b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
394b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
395b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    private TimerObj getNextRunningTimer(
396b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            ArrayList<TimerObj> timers, boolean requireNextUpdate, long now) {
397b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        long nextTimesup = Long.MAX_VALUE;
398b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        boolean nextTimerFound = false;
399b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        Iterator<TimerObj> i = timers.iterator();
400b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        TimerObj t = null;
401b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        while(i.hasNext()) {
402b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            TimerObj tmp = i.next();
403b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            if (tmp.mState == TimerObj.STATE_RUNNING) {
404b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                long timesupTime = tmp.getTimesupTime();
405b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                long timeLeft = timesupTime - now;
406b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                if (timesupTime < nextTimesup && (!requireNextUpdate || timeLeft > 60) ) {
407b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    nextTimesup = timesupTime;
408b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    nextTimerFound = true;
409b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                    t = tmp;
410b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein                }
411b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            }
412b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
413b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        if (nextTimerFound) {
414b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return t;
415b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        } else {
416b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein            return null;
417b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein        }
418b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
419b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein
4205f594531901d5f562c0c6a8e1d4ffd2e57709764Daria Evdokimova    public static void cancelInUseNotification(final Context context) {
421b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        NotificationManagerCompat.from(context).cancel(IN_USE_NOTIFICATION_ID);
422b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein    }
423e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
424e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    private void showTimesUpNotification(final Context context) {
425e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        for (TimerObj timerObj : Timers.timersInTimesUp(mTimers) ) {
426e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            showTimesUpNotification(context, timerObj);
427e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
428e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
429e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
430e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    private void showTimesUpNotification(final Context context, TimerObj timerObj) {
431e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Content Intent. When clicked will show the timer full screen
432e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        PendingIntent contentIntent = PendingIntent.getActivity(context, timerObj.mTimerId,
433e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                new Intent(context, TimerAlertFullScreen.class).putExtra(
434e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        Timers.TIMER_INTENT_EXTRA, timerObj.mTimerId),
435e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                PendingIntent.FLAG_UPDATE_CURRENT);
436e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
437e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Add one minute action button
438e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        PendingIntent addOneMinuteAction = PendingIntent.getBroadcast(context, timerObj.mTimerId,
439e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                new Intent(Timers.NOTIF_TIMES_UP_PLUS_ONE)
440e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        .putExtra(Timers.TIMER_INTENT_EXTRA, timerObj.mTimerId),
441e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                PendingIntent.FLAG_UPDATE_CURRENT);
442e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
443e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Add stop/done action button
444d50ff479c37e33de776c339bc93df308f4ac7ab0rachelzhang        PendingIntent stopIntent = PendingIntent.getBroadcast(context, timerObj.mTimerId,
445e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                new Intent(Timers.NOTIF_TIMES_UP_STOP)
446e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        .putExtra(Timers.TIMER_INTENT_EXTRA, timerObj.mTimerId),
447e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                PendingIntent.FLAG_UPDATE_CURRENT);
448e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
449e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Notification creation
450b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
451e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setContentIntent(contentIntent)
452c117a1900fa194ee8822b376186b320d61c38e33Annie Chin                .addAction(R.drawable.ic_add_24dp,
453e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        context.getResources().getString(R.string.timer_plus_1_min),
454e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        addOneMinuteAction)
455e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .addAction(
456e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        timerObj.getDeleteAfterUse()
457e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                                ? android.R.drawable.ic_menu_close_clear_cancel
4587f5209b10f08a3919245552516e60b4f07982a04Annie Chin                                : R.drawable.ic_stop_24dp,
459e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                        timerObj.getDeleteAfterUse()
460e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                                ? context.getResources().getString(R.string.timer_done)
461c5deecb3c25796538d762857692182bf3e5f1e21rachelzhang                                : context.getResources().getString(R.string.timer_stop),
462d50ff479c37e33de776c339bc93df308f4ac7ab0rachelzhang                        stopIntent)
463e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setContentTitle(timerObj.getLabelOrDefault(context))
464e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setContentText(context.getResources().getString(R.string.timer_times_up))
465e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setSmallIcon(R.drawable.stat_notify_timer)
466e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setOngoing(true)
467e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                .setAutoCancel(false)
468b7224ca55c54d7298d50f28f031197f442318a19Annie Chin                .setPriority(NotificationCompat.PRIORITY_MAX)
469b7224ca55c54d7298d50f28f031197f442318a19Annie Chin                .setDefaults(NotificationCompat.DEFAULT_LIGHTS)
47079c464e19f63a44a416eb4844702a7ba772b9072Annie Chin                .setWhen(0);
47179c464e19f63a44a416eb4844702a7ba772b9072Annie Chin
472e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // Send the notification using the timer's id to identify the
473e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        // correct notification
474b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        NotificationManagerCompat.from(context).notify(timerObj.mTimerId, builder.build());
475e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (Timers.LOGGING) {
476e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            Log.v(TAG, "Setting times-up notification for "
477e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                    + timerObj.getLabelOrDefault(context) + " #" + timerObj.mTimerId);
478e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
479e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
480e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
481e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    private void cancelTimesUpNotification(final Context context) {
482e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        for (TimerObj timerObj : Timers.timersInTimesUp(mTimers) ) {
483e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            cancelTimesUpNotification(context, timerObj);
484e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
485e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
486e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
487e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    private void cancelTimesUpNotification(final Context context, TimerObj timerObj) {
488b7224ca55c54d7298d50f28f031197f442318a19Annie Chin        NotificationManagerCompat.from(context).cancel(timerObj.mTimerId);
489e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        if (Timers.LOGGING) {
490e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas            Log.v(TAG, "Canceling times-up notification for "
491e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas                    + timerObj.getLabelOrDefault(context) + " #" + timerObj.mTimerId);
492e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        }
493e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
49498b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas}
495