TimerReceiver.java revision 4fd80b58da8ad66e0e4a23cbf7c900174727fb4e
1815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson/* 2815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * Copyright (C) 2012 The Android Open Source Project 3815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * 4815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * Licensed under the Apache License, Version 2.0 (the "License"); 5815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * you may not use this file except in compliance with the License. 6815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * You may obtain a copy of the License at 7815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * 8815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * http://www.apache.org/licenses/LICENSE-2.0 9815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * 10815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * Unless required by applicable law or agreed to in writing, software 11815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * distributed under the License is distributed on an "AS IS" BASIS, 12815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * See the License for the specific language governing permissions and 14815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson * limitations under the License. 15815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson */ 16815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson 17815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonpackage com.android.deskclock.timer; 18815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson 19dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport android.app.AlarmManager; 20815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.app.Notification; 21b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzsteinimport android.app.NotificationManager; 22815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.app.PendingIntent; 23815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.content.BroadcastReceiver; 24815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.content.Context; 25815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.content.Intent; 26dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport android.content.SharedPreferences; 27dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport android.preference.PreferenceManager; 28815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonimport android.util.Log; 29815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson 30dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport com.android.deskclock.DeskClock; 31b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzsteinimport com.android.deskclock.R; 325cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport com.android.deskclock.TimerRingService; 337cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelsonimport com.android.deskclock.Utils; 34dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson 35dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport java.util.ArrayList; 36dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelsonimport java.util.Iterator; 37dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson 38815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelsonpublic class TimerReceiver extends BroadcastReceiver { 39815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson private static final String TAG = "TimerReceiver"; 40815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson 4158c329f52d7952597ae7a4e88fc2ec9e15ddd93aRobyn Coultas // Make this a large number to avoid the alarm ID's which seem to be 1, 2, ... 4258c329f52d7952597ae7a4e88fc2ec9e15ddd93aRobyn Coultas // Must also be different than StopwatchService.NOTIFICATION_ID 4358c329f52d7952597ae7a4e88fc2ec9e15ddd93aRobyn Coultas private static final int IN_USE_NOTIFICATION_ID = Integer.MAX_VALUE - 2; 44b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 45dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson ArrayList<TimerObj> mTimers; 46dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson 47dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson @Override 48815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson public void onReceive(final Context context, final Intent intent) { 49b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein int timer; 50b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein String actionType = intent.getAction(); 51815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson 52b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein // Get the updated timers data. 53b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (mTimers == null) { 54b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein mTimers = new ArrayList<TimerObj> (); 55b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 56b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 57b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein TimerObj.getTimersFromSharedPrefs(prefs, mTimers); 58815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson 59815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson 60b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (intent.hasExtra(Timers.TIMER_INTENT_EXTRA)) { 61b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein // Get the alarm out of the Intent 62b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein timer = intent.getIntExtra(Timers.TIMER_INTENT_EXTRA, -1); 63b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (timer == -1) { 64b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein Log.d(TAG, " got intent without Timer data: "+actionType); 65b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 66b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } else if (Timers.NOTIF_IN_USE_SHOW.equals(actionType)){ 67b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein showInUseNotification(context); 68b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein return; 69b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } else if (Timers.NOTIF_IN_USE_CANCEL.equals(actionType)) { 70b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein cancelInUseNotification(context); 71b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein return; 72b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } else { 73b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein // No data to work with, do nothing 74b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein Log.d(TAG, " got intent without Timer data"); 75b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein return; 76b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 77b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 78b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein TimerObj t = Timers.findTimer(mTimers, timer); 79815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson 80b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (intent.getBooleanExtra(Timers.UPDATE_NOTIFICATION, false)) { 81b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (Timers.TIMER_STOP.equals(actionType)) { 82b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (t == null) { 83b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein Log.d(TAG, "timer not found in list - can't stop it."); 84b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein return; 85b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 86b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein t.mState = TimerObj.STATE_DONE; 87b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein t.writeToSharedPref(prefs); 88b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein SharedPreferences.Editor editor = prefs.edit(); 89b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein editor.putBoolean(Timers.FROM_NOTIFICATION, true); 907cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelson editor.putLong(Timers.NOTIF_TIME, Utils.getTimeNow()); 91b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein editor.putInt(Timers.NOTIF_ID, timer); 92b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein editor.apply(); 93dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson 94b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein stopRingtoneIfNoTimesup(context); 95dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson 96b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein Intent activityIntent = new Intent(context, DeskClock.class); 97b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 98b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein activityIntent.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX); 99b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein context.startActivity(activityIntent); 100b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 101b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein return; 102b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 103b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 104b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (Timers.TIMES_UP.equals(actionType)) { 105dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson // Find the timer (if it doesn't exists, it was probably deleted). 106dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson if (t == null) { 107dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson Log.d(TAG, " timer not found in list - do nothing"); 108dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson return; 109dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson } 110dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson 111dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson t.mState = TimerObj.STATE_TIMESUP; 112dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson t.writeToSharedPref(prefs); 113733afe52e37148f2f1d36436c1a1eadcad5b471bRobyn Coultas // Play ringtone by using TimerRingService service with a default alarm. 114dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson Log.d(TAG, "playing ringtone"); 115dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson Intent si = new Intent(); 1165cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson si.setClass(context, TimerRingService.class); 117dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson context.startService(si); 118dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson 11998b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas // Update the in-use notification 1207cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelson if (getNextRunningTimer(mTimers, false, Utils.getTimeNow()) == null) { 121b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein // Found no running timers. 122b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein cancelInUseNotification(context); 12398b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas } else { 12498b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas showInUseNotification(context); 125dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson } 126b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 12798b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas // Start the TimerAlertFullScreen activity. 12898b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas Intent timersAlert = new Intent(context, TimerAlertFullScreen.class); 12998b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas timersAlert.setFlags( 13098b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION); 13198b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas context.startActivity(timersAlert); 132b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } else if (Timers.TIMER_RESET.equals(actionType) 133b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein || Timers.DELETE_TIMER.equals(actionType) 134b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein || Timers.TIMER_DONE.equals(actionType)) { 13598b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas // Stop Ringtone if all timers are not in times-up status 136b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein stopRingtoneIfNoTimesup(context); 137dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson } 138dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson // Update the next "Times up" alarm 139b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein updateNextTimesup(context); 140b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 141b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 142b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein private void stopRingtoneIfNoTimesup(final Context context) { 143b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (Timers.findExpiredTimer(mTimers) == null) { 144b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein // Stop ringtone 145b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein Log.d(TAG, "stopping ringtone"); 146b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein Intent si = new Intent(); 147b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein si.setClass(context, TimerRingService.class); 148b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein context.stopService(si); 149b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 150815e2f7431c590086d5bd4eee5d7bf08108c77ebIsaac Katzenelson } 151dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson 152dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson // Scan all timers and find the one that will expire next. 153dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson // Tell AlarmManager to send a "Time's up" message to this receiver when this timer expires. 154dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson // If no timer exists, clear "time's up" message. 155dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson private void updateNextTimesup(Context context) { 1567cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelson TimerObj t = getNextRunningTimer(mTimers, false, Utils.getTimeNow()); 157b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein long nextTimesup = (t == null) ? -1 : t.getTimesupTime(); 158b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein int timerId = (t == null) ? -1 : t.mTimerId; 159dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson 160dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson Intent intent = new Intent(); 161dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson intent.setAction(Timers.TIMES_UP); 162dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson intent.setClass(context, TimerReceiver.class); 163b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (!mTimers.isEmpty()) { 164b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein intent.putExtra(Timers.TIMER_INTENT_EXTRA, timerId); 165dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson } 166dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson AlarmManager mngr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); 167dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson PendingIntent p = PendingIntent.getBroadcast(context, 168dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); 169b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (t != null) { 1704fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas if (Utils.isKeyLimePieOrLater()) { 1714fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas mngr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTimesup, p); 1724fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas } else { 1734fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas mngr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTimesup, p); 1744fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas } 175dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson Log.d(TAG,"Setting times up to " + nextTimesup); 176dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson } else { 177dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson Log.d(TAG,"canceling times up"); 178dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson mngr.cancel(p); 179dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson } 180dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson } 181dfb182f455aa07eb7319d32df6d9a14492884e2fIsaac Katzenelson 182b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein private void showInUseNotification(final Context context) { 183b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 184b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein boolean appOpen = prefs.getBoolean(Timers.NOTIF_APP_OPEN, false); 185b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein ArrayList<TimerObj> timersInUse = Timers.timersInUse(mTimers); 186b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein int numTimersInUse = timersInUse.size(); 187b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 188b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (appOpen || numTimersInUse == 0) { 189b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein return; 190b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 191b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 192b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein String title, contentText; 193b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein Long nextBroadcastTime = null; 1947cff0e83debe70133aad4cf22163263b16d4a255Isaac Katzenelson long now = Utils.getTimeNow(); 195b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (timersInUse.size() == 1) { 196b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein TimerObj timer = timersInUse.get(0); 197d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein boolean timerIsTicking = timer.isTicking(); 19872332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein String label = timer.mLabel.equals("") ? 19972332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein context.getString(R.string.timer_notification_label) : timer.mLabel; 200d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein title = timerIsTicking ? label : context.getString(R.string.timer_stopped); 201d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein long timeLeft = timerIsTicking ? timer.getTimesupTime() - now : timer.mTimeLeft; 202b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein contentText = buildTimeRemaining(context, timeLeft); 203d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein if (timerIsTicking && timeLeft > 60 * 1000) { 204b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein nextBroadcastTime = getBroadcastTime(now, timeLeft); 205b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 206b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } else { 207b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein TimerObj timer = getNextRunningTimer(timersInUse, false, now); 208b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (timer == null) { 209b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein // No running timers. 210b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein title = String.format( 211b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein context.getString(R.string.timers_stopped), numTimersInUse); 212b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein contentText = context.getString(R.string.all_timers_stopped_notif); 213b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } else { 214b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein // We have at least one timer running and other timers stopped. 215b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein title = String.format( 216b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein context.getString(R.string.timers_in_use), numTimersInUse); 217b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein long completionTime = timer.getTimesupTime(); 218b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein long timeLeft = completionTime - now; 219b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein contentText = String.format(context.getString(R.string.next_timer_notif), 220b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein buildTimeRemaining(context, timeLeft)); 221d82ec8feb07d6d1f0f9539f75b1b84b3ae1e74d4Sam Blitzstein if (timeLeft <= 60 * 1000) { 222b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein TimerObj timerWithUpdate = getNextRunningTimer(timersInUse, true, now); 223b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (timerWithUpdate != null) { 224b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein completionTime = timerWithUpdate.getTimesupTime(); 225b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein timeLeft = completionTime - now; 226b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein nextBroadcastTime = getBroadcastTime(now, timeLeft); 227b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 228b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } else { 229b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein nextBroadcastTime = getBroadcastTime(now, timeLeft); 230b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 231b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 232b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 233b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein showCollapsedNotificationWithNext(context, title, contentText, nextBroadcastTime); 234b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 235b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 236b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein private long getBroadcastTime(long now, long timeUntilBroadcast) { 237b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein long seconds = timeUntilBroadcast / 1000; 238b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein seconds = seconds - ( (seconds / 60) * 60 ); 239b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein return now + (seconds * 1000); 240b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 241b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 24272332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein /** Public and static to allow timer fragment to update notification with new label. **/ 24372332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein public static void showExpiredAlarmNotification(Context context, TimerObj t) { 24472332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein Intent broadcastIntent = new Intent(); 24572332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein broadcastIntent.putExtra(Timers.TIMER_INTENT_EXTRA, t.mTimerId); 24672332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein broadcastIntent.setAction(Timers.TIMER_STOP); 24772332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein broadcastIntent.putExtra(Timers.UPDATE_NOTIFICATION, true); 24872332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein PendingIntent pendingBroadcastIntent = PendingIntent.getBroadcast( 24972332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein context, 0, broadcastIntent, 0); 25072332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein String label = t.mLabel.equals("") ? context.getString(R.string.timer_notification_label) : 25172332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein t.mLabel; 25272332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein String contentText = context.getString(R.string.timer_times_up); 25372332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein showCollapsedNotification(context, label, contentText, Notification.PRIORITY_MAX, 25472332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein pendingBroadcastIntent, t.mTimerId, true); 25572332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein } 25672332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein 257b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein private void showCollapsedNotificationWithNext( 258b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein final Context context, String title, String text, Long nextBroadcastTime) { 259b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein Intent activityIntent = new Intent(context, DeskClock.class); 260b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 261b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein activityIntent.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX); 262b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein PendingIntent pendingActivityIntent = PendingIntent.getActivity(context, 0, activityIntent, 263b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); 264b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein showCollapsedNotification(context, title, text, Notification.PRIORITY_HIGH, 265184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein pendingActivityIntent, IN_USE_NOTIFICATION_ID, false); 266b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 267b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (nextBroadcastTime == null) { 268b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein return; 269b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 270b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein Intent nextBroadcast = new Intent(); 271b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein nextBroadcast.setAction(Timers.NOTIF_IN_USE_SHOW); 272b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein PendingIntent pendingNextBroadcast = 273b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein PendingIntent.getBroadcast(context, 0, nextBroadcast, 0); 274b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein AlarmManager alarmManager = 275b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 2764fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas if (Utils.isKeyLimePieOrLater()) { 2774fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas alarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextBroadcastTime, pendingNextBroadcast); 2784fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas } else { 2794fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas alarmManager.set(AlarmManager.ELAPSED_REALTIME, nextBroadcastTime, pendingNextBroadcast); 2804fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas } 281b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 282b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 28372332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein private static void showCollapsedNotification(final Context context, String title, String text, 284184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein int priority, PendingIntent pendingIntent, int notificationId, boolean showTicker) { 285184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein Notification.Builder builder = new Notification.Builder(context) 286b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein .setAutoCancel(false) 287b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein .setContentTitle(title) 288b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein .setContentText(text) 289b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein .setDeleteIntent(pendingIntent) 290b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein .setOngoing(true) 291b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein .setPriority(priority) 292b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein .setShowWhen(false) 293184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein .setSmallIcon(R.drawable.stat_notify_timer); 294184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein if (showTicker) { 295184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein builder.setTicker(text); 296184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein } 297184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein 298184aa695ef173081a692be38d9b23560ad34b789Sam Blitzstein Notification notification = builder.build(); 299b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein notification.contentIntent = pendingIntent; 300b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein NotificationManager notificationManager = 301b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 302b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein notificationManager.notify(notificationId, notification); 303b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 304b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 305b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein private String buildTimeRemaining(Context context, long timeLeft) { 306b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (timeLeft < 0) { 307b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein // We should never be here... 308b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein Log.v(TAG, "Will not show notification for timer already expired."); 309b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein return null; 310b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 311b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 312b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein long hundreds, seconds, minutes, hours; 313b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein seconds = timeLeft / 1000; 314b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein minutes = seconds / 60; 315b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein seconds = seconds - minutes * 60; 316b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein hours = minutes / 60; 317b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein minutes = minutes - hours * 60; 318b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (hours > 99) { 319b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein hours = 0; 320b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 321b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 322b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein String hourSeq = (hours == 0) ? "" : 323b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein ( (hours == 1) ? context.getString(R.string.hour) : 324b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein context.getString(R.string.hours, Long.toString(hours)) ); 325b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein String minSeq = (minutes == 0) ? "" : 326b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein ( (minutes == 1) ? context.getString(R.string.minute) : 327b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein context.getString(R.string.minutes, Long.toString(minutes)) ); 328b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 329b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein boolean dispHour = hours > 0; 330b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein boolean dispMinute = minutes > 0; 331b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein int index = (dispHour ? 1 : 0) | (dispMinute ? 2 : 0); 332b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein String[] formats = context.getResources().getStringArray(R.array.timer_notifications); 333b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein return String.format(formats[index], hourSeq, minSeq); 334b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 335b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 336b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein private TimerObj getNextRunningTimer( 337b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein ArrayList<TimerObj> timers, boolean requireNextUpdate, long now) { 338b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein long nextTimesup = Long.MAX_VALUE; 339b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein boolean nextTimerFound = false; 340b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein Iterator<TimerObj> i = timers.iterator(); 341b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein TimerObj t = null; 342b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein while(i.hasNext()) { 343b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein TimerObj tmp = i.next(); 344b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (tmp.mState == TimerObj.STATE_RUNNING) { 345b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein long timesupTime = tmp.getTimesupTime(); 346b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein long timeLeft = timesupTime - now; 347b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (timesupTime < nextTimesup && (!requireNextUpdate || timeLeft > 60) ) { 348b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein nextTimesup = timesupTime; 349b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein nextTimerFound = true; 350b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein t = tmp; 351b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 352b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 353b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 354b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein if (nextTimerFound) { 355b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein return t; 356b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } else { 357b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein return null; 358b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 359b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 360b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein 361b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein private void cancelInUseNotification(final Context context) { 362b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein NotificationManager notificationManager = 363b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 364b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein notificationManager.cancel(IN_USE_NOTIFICATION_ID); 365b6a815a52fd31a96b51e51ef6275a68bed73c2c5Sam Blitzstein } 36698b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas} 367