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