163211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson/* 263211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson * Copyright (C) 2012 The Android Open Source Project 363211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson * 463211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson * Licensed under the Apache License, Version 2.0 (the "License"); 563211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson * you may not use this file except in compliance with the License. 663211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson * You may obtain a copy of the License at 763211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson * 863211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson * http://www.apache.org/licenses/LICENSE-2.0 963211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson * 1063211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson * Unless required by applicable law or agreed to in writing, software 1163211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson * distributed under the License is distributed on an "AS IS" BASIS, 1263211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1363211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson * See the License for the specific language governing permissions and 1463211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson * limitations under the License. 1563211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson */ 1663211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson 1763211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelsonpackage com.android.deskclock.timer; 1863211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson 19871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelsonimport android.app.AlarmManager; 2063211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelsonimport android.app.Notification; 211ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelsonimport android.app.NotificationManager; 2263211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelsonimport android.app.PendingIntent; 2363211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelsonimport android.content.BroadcastReceiver; 2463211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelsonimport android.content.Context; 2563211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelsonimport android.content.Intent; 26871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelsonimport android.content.SharedPreferences; 27871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelsonimport android.preference.PreferenceManager; 2863211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelsonimport android.util.Log; 2963211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson 30871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelsonimport com.android.deskclock.DeskClock; 311ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelsonimport com.android.deskclock.R; 323f99104b4dc4df9a774ff7433084b8e483d88744Isaac Katzenelsonimport com.android.deskclock.TimerRingService; 33a65d3eb00d543735305221f86ce630c8650e65e2Isaac Katzenelsonimport com.android.deskclock.Utils; 34871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson 35871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelsonimport java.util.ArrayList; 36871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelsonimport java.util.Iterator; 37871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson 3863211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelsonpublic class TimerReceiver extends BroadcastReceiver { 3963211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson private static final String TAG = "TimerReceiver"; 4063211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson 419e33fe399ce41766dc0a2d6e8f3e2fef0d30f6c1Robyn Coultas // Make this a large number to avoid the alarm ID's which seem to be 1, 2, ... 429e33fe399ce41766dc0a2d6e8f3e2fef0d30f6c1Robyn Coultas // Must also be different than StopwatchService.NOTIFICATION_ID 439e33fe399ce41766dc0a2d6e8f3e2fef0d30f6c1Robyn Coultas private static final int IN_USE_NOTIFICATION_ID = Integer.MAX_VALUE - 2; 441ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 45871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson ArrayList<TimerObj> mTimers; 46871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson 47871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson @Override 4863211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson public void onReceive(final Context context, final Intent intent) { 49dd97056fa889b35ce850fd0107e2f59b925bae49Isaac Katzenelson if (Timers.LOGGING) { 50dd97056fa889b35ce850fd0107e2f59b925bae49Isaac Katzenelson Log.v(TAG, "Received intent " + intent.toString()); 51dd97056fa889b35ce850fd0107e2f59b925bae49Isaac Katzenelson } 521ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson String actionType = intent.getAction(); 53d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // This action does not need the timers data 54d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas if (Timers.NOTIF_IN_USE_CANCEL.equals(actionType)) { 55d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas cancelInUseNotification(context); 56d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas return; 57d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 58d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 591ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson // Get the updated timers data. 601ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (mTimers == null) { 611ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson mTimers = new ArrayList<TimerObj> (); 621ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 631ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 641ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson TimerObj.getTimersFromSharedPrefs(prefs, mTimers); 6563211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson 66d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // These actions do not provide a timer ID, but do use the timers data 67d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas if (Timers.NOTIF_IN_USE_SHOW.equals(actionType)) { 681ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson showInUseNotification(context); 691ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson return; 70d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } else if (Timers.NOTIF_TIMES_UP_SHOW.equals(actionType)) { 71d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas showTimesUpNotification(context); 721ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson return; 73d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } else if (Timers.NOTIF_TIMES_UP_CANCEL.equals(actionType)) { 74d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas cancelTimesUpNotification(context); 751ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson return; 761ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 771ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 78d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Remaining actions provide a timer Id 79d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas if (!intent.hasExtra(Timers.TIMER_INTENT_EXTRA)) { 80d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // No data to work with, do nothing 81dd97056fa889b35ce850fd0107e2f59b925bae49Isaac Katzenelson Log.e(TAG, "got intent without Timer data"); 82d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas return; 83d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 8463211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson 85d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Get the timer out of the Intent 86d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas int timerId = intent.getIntExtra(Timers.TIMER_INTENT_EXTRA, -1); 87d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas if (timerId == -1) { 88d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas Log.d(TAG, "OnReceive:intent without Timer data for " + actionType); 891ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 90dd97056fa889b35ce850fd0107e2f59b925bae49Isaac Katzenelson 91d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas TimerObj t = Timers.findTimer(mTimers, timerId); 921ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 931ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (Timers.TIMES_UP.equals(actionType)) { 94871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson // Find the timer (if it doesn't exists, it was probably deleted). 95871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson if (t == null) { 96871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson Log.d(TAG, " timer not found in list - do nothing"); 97871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson return; 98871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson } 99871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson 100871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson t.mState = TimerObj.STATE_TIMESUP; 101871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson t.writeToSharedPref(prefs); 102647353dd437f8c075aa96f3b98efb84442e8b7bfRobyn Coultas // Play ringtone by using TimerRingService service with a default alarm. 103871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson Log.d(TAG, "playing ringtone"); 104871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson Intent si = new Intent(); 1053f99104b4dc4df9a774ff7433084b8e483d88744Isaac Katzenelson si.setClass(context, TimerRingService.class); 106871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson context.startService(si); 107871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson 108feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas // Update the in-use notification 109a65d3eb00d543735305221f86ce630c8650e65e2Isaac Katzenelson if (getNextRunningTimer(mTimers, false, Utils.getTimeNow()) == null) { 1101ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson // Found no running timers. 1111ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson cancelInUseNotification(context); 112feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas } else { 113feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas showInUseNotification(context); 114871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson } 1151ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 116feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas // Start the TimerAlertFullScreen activity. 117feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas Intent timersAlert = new Intent(context, TimerAlertFullScreen.class); 118feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas timersAlert.setFlags( 119feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION); 120feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas context.startActivity(timersAlert); 1211ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } else if (Timers.TIMER_RESET.equals(actionType) 1221ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson || Timers.DELETE_TIMER.equals(actionType) 1231ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson || Timers.TIMER_DONE.equals(actionType)) { 124feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas // Stop Ringtone if all timers are not in times-up status 1251ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson stopRingtoneIfNoTimesup(context); 126d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } else if (Timers.NOTIF_TIMES_UP_STOP.equals(actionType)) { 127d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Find the timer (if it doesn't exists, it was probably deleted). 128d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas if (t == null) { 129d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas Log.d(TAG, "timer to stop not found in list - do nothing"); 130d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas return; 131d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } else if (t.mState != TimerObj.STATE_TIMESUP) { 132d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas Log.d(TAG, "action to stop but timer not in times-up state - do nothing"); 133d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas return; 134d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 135d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 136d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Update timer state 137d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas t.mState = t.getDeleteAfterUse() ? TimerObj.STATE_DELETED : TimerObj.STATE_DONE; 138d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas t.mTimeLeft = t.mOriginalLength - (Utils.getTimeNow() - t.mStartTime); 139d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas t.writeToSharedPref(prefs); 140d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 141d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Flag to tell DeskClock to re-sync with the database 142d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas prefs.edit().putBoolean(Timers.FROM_NOTIFICATION, true).apply(); 143d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 144d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas cancelTimesUpNotification(context, t); 145d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 146d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Done with timer - delete from data base 147d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas if (t.getDeleteAfterUse()) { 148d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas t.deleteFromSharedPref(prefs); 149d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 150d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 151d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Stop Ringtone if no timers are in times-up status 152d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas stopRingtoneIfNoTimesup(context); 153d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } else if (Timers.NOTIF_TIMES_UP_PLUS_ONE.equals(actionType)) { 154d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Find the timer (if it doesn't exists, it was probably deleted). 155d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas if (t == null) { 156d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas Log.d(TAG, "timer to +1m not found in list - do nothing"); 157d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas return; 158d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } else if (t.mState != TimerObj.STATE_TIMESUP) { 159d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas Log.d(TAG, "action to +1m but timer not in times up state - do nothing"); 160d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas return; 161d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 162d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 163d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Restarting the timer with 1 minute left. 164d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas t.mState = TimerObj.STATE_RUNNING; 165d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas t.mStartTime = Utils.getTimeNow(); 166d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas t.mTimeLeft = t. mOriginalLength = TimerObj.MINUTE_IN_MILLIS; 167d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas t.writeToSharedPref(prefs); 168d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 169d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Flag to tell DeskClock to re-sync with the database 170d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas prefs.edit().putBoolean(Timers.FROM_NOTIFICATION, true).apply(); 171d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 172d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas cancelTimesUpNotification(context, t); 173d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 174d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // If the app is not open, refresh the in-use notification 175d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas if (!prefs.getBoolean(Timers.NOTIF_APP_OPEN, false)) { 176d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas showInUseNotification(context); 177d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 178d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 179d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Stop Ringtone if no timers are in times-up status 180d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas stopRingtoneIfNoTimesup(context); 181d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } else if (Timers.TIMER_UPDATE.equals(actionType)) { 182d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Refresh buzzing notification 183d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas if (t.mState == TimerObj.STATE_TIMESUP) { 184d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Must cancel the previous notification to get all updates displayed correctly 185d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas cancelTimesUpNotification(context, t); 186d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas showTimesUpNotification(context, t); 187d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 188871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson } 189871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson // Update the next "Times up" alarm 1901ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson updateNextTimesup(context); 1911ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 1921ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 1931ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson private void stopRingtoneIfNoTimesup(final Context context) { 1941ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (Timers.findExpiredTimer(mTimers) == null) { 1951ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson // Stop ringtone 1961ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson Log.d(TAG, "stopping ringtone"); 1971ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson Intent si = new Intent(); 1981ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson si.setClass(context, TimerRingService.class); 1991ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson context.stopService(si); 2001ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 20163211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson } 202871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson 203871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson // Scan all timers and find the one that will expire next. 204871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson // Tell AlarmManager to send a "Time's up" message to this receiver when this timer expires. 205871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson // If no timer exists, clear "time's up" message. 206871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson private void updateNextTimesup(Context context) { 207a65d3eb00d543735305221f86ce630c8650e65e2Isaac Katzenelson TimerObj t = getNextRunningTimer(mTimers, false, Utils.getTimeNow()); 2081ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson long nextTimesup = (t == null) ? -1 : t.getTimesupTime(); 2091ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson int timerId = (t == null) ? -1 : t.mTimerId; 210871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson 211871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson Intent intent = new Intent(); 212871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson intent.setAction(Timers.TIMES_UP); 213871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson intent.setClass(context, TimerReceiver.class); 2141ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (!mTimers.isEmpty()) { 2151ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson intent.putExtra(Timers.TIMER_INTENT_EXTRA, timerId); 216871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson } 217871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson AlarmManager mngr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); 218871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson PendingIntent p = PendingIntent.getBroadcast(context, 219871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); 2201ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (t != null) { 2216ff2962d9a2726bacffa20ed09e3c3346fe7491dIsaac Katzenelson if (Utils.isKitKatOrLater()) { 222203bf487b2648df2edc74787d6bf2ab34d24191fRobyn Coultas mngr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTimesup, p); 223203bf487b2648df2edc74787d6bf2ab34d24191fRobyn Coultas } else { 224203bf487b2648df2edc74787d6bf2ab34d24191fRobyn Coultas mngr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTimesup, p); 225203bf487b2648df2edc74787d6bf2ab34d24191fRobyn Coultas } 226d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas if (Timers.LOGGING) { 227d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas Log.d(TAG, "Setting times up to " + nextTimesup); 228d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 229871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson } else { 230871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson mngr.cancel(p); 231d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas if (Timers.LOGGING) { 232d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas Log.v(TAG, "no next times up"); 233d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 234871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson } 235871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson } 236871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson 2371ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson private void showInUseNotification(final Context context) { 2381ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 2391ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson boolean appOpen = prefs.getBoolean(Timers.NOTIF_APP_OPEN, false); 2401ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson ArrayList<TimerObj> timersInUse = Timers.timersInUse(mTimers); 2411ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson int numTimersInUse = timersInUse.size(); 2421ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 2431ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (appOpen || numTimersInUse == 0) { 2441ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson return; 2451ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 2461ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 2471ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson String title, contentText; 2481ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson Long nextBroadcastTime = null; 249a65d3eb00d543735305221f86ce630c8650e65e2Isaac Katzenelson long now = Utils.getTimeNow(); 2501ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (timersInUse.size() == 1) { 2511ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson TimerObj timer = timersInUse.get(0); 252f6017873e1056b0d9ec0932eb49af77f8ce8814cSam Blitzstein boolean timerIsTicking = timer.isTicking(); 253d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas String label = timer.getLabelOrDefault(context); 254f6017873e1056b0d9ec0932eb49af77f8ce8814cSam Blitzstein title = timerIsTicking ? label : context.getString(R.string.timer_stopped); 255f6017873e1056b0d9ec0932eb49af77f8ce8814cSam Blitzstein long timeLeft = timerIsTicking ? timer.getTimesupTime() - now : timer.mTimeLeft; 2561ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson contentText = buildTimeRemaining(context, timeLeft); 257b2e25c2811320be6f719d3dfe9931cdc06212759Isaac Katzenelson if (timerIsTicking && timeLeft > TimerObj.MINUTE_IN_MILLIS) { 2581ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson nextBroadcastTime = getBroadcastTime(now, timeLeft); 2591ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 2601ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } else { 2611ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson TimerObj timer = getNextRunningTimer(timersInUse, false, now); 2621ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (timer == null) { 2631ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson // No running timers. 2641ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson title = String.format( 2651ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson context.getString(R.string.timers_stopped), numTimersInUse); 2661ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson contentText = context.getString(R.string.all_timers_stopped_notif); 2671ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } else { 2681ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson // We have at least one timer running and other timers stopped. 2691ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson title = String.format( 2701ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson context.getString(R.string.timers_in_use), numTimersInUse); 2711ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson long completionTime = timer.getTimesupTime(); 2721ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson long timeLeft = completionTime - now; 2731ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson contentText = String.format(context.getString(R.string.next_timer_notif), 2741ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson buildTimeRemaining(context, timeLeft)); 275b2e25c2811320be6f719d3dfe9931cdc06212759Isaac Katzenelson if (timeLeft <= TimerObj.MINUTE_IN_MILLIS) { 2761ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson TimerObj timerWithUpdate = getNextRunningTimer(timersInUse, true, now); 2771ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (timerWithUpdate != null) { 2781ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson completionTime = timerWithUpdate.getTimesupTime(); 2791ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson timeLeft = completionTime - now; 2801ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson nextBroadcastTime = getBroadcastTime(now, timeLeft); 2811ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 2821ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } else { 2831ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson nextBroadcastTime = getBroadcastTime(now, timeLeft); 2841ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 2851ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 2861ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 2871ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson showCollapsedNotificationWithNext(context, title, contentText, nextBroadcastTime); 2881ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 2891ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 2901ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson private long getBroadcastTime(long now, long timeUntilBroadcast) { 2911ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson long seconds = timeUntilBroadcast / 1000; 2921ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson seconds = seconds - ( (seconds / 60) * 60 ); 2931ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson return now + (seconds * 1000); 2941ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 2951ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 2961ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson private void showCollapsedNotificationWithNext( 2971ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson final Context context, String title, String text, Long nextBroadcastTime) { 2981ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson Intent activityIntent = new Intent(context, DeskClock.class); 2991ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 3001ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson activityIntent.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX); 3011ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson PendingIntent pendingActivityIntent = PendingIntent.getActivity(context, 0, activityIntent, 3021ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); 3031ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson showCollapsedNotification(context, title, text, Notification.PRIORITY_HIGH, 30406077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein pendingActivityIntent, IN_USE_NOTIFICATION_ID, false); 3051ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 3061ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (nextBroadcastTime == null) { 3071ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson return; 3081ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 3091ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson Intent nextBroadcast = new Intent(); 3101ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson nextBroadcast.setAction(Timers.NOTIF_IN_USE_SHOW); 3111ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson PendingIntent pendingNextBroadcast = 3121ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson PendingIntent.getBroadcast(context, 0, nextBroadcast, 0); 3131ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson AlarmManager alarmManager = 3141ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 3156ff2962d9a2726bacffa20ed09e3c3346fe7491dIsaac Katzenelson if (Utils.isKitKatOrLater()) { 316203bf487b2648df2edc74787d6bf2ab34d24191fRobyn Coultas alarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextBroadcastTime, pendingNextBroadcast); 317203bf487b2648df2edc74787d6bf2ab34d24191fRobyn Coultas } else { 318203bf487b2648df2edc74787d6bf2ab34d24191fRobyn Coultas alarmManager.set(AlarmManager.ELAPSED_REALTIME, nextBroadcastTime, pendingNextBroadcast); 319203bf487b2648df2edc74787d6bf2ab34d24191fRobyn Coultas } 3201ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 3211ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 3220f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein private static void showCollapsedNotification(final Context context, String title, String text, 32306077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein int priority, PendingIntent pendingIntent, int notificationId, boolean showTicker) { 32406077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein Notification.Builder builder = new Notification.Builder(context) 3251ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson .setAutoCancel(false) 3261ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson .setContentTitle(title) 3271ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson .setContentText(text) 3281ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson .setDeleteIntent(pendingIntent) 3291ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson .setOngoing(true) 3301ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson .setPriority(priority) 3311ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson .setShowWhen(false) 33206077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein .setSmallIcon(R.drawable.stat_notify_timer); 33306077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein if (showTicker) { 33406077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein builder.setTicker(text); 33506077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein } 33606077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein 33706077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein Notification notification = builder.build(); 3381ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson notification.contentIntent = pendingIntent; 3391ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson NotificationManager notificationManager = 3401ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 3411ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson notificationManager.notify(notificationId, notification); 3421ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 3431ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 3441ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson private String buildTimeRemaining(Context context, long timeLeft) { 3451ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (timeLeft < 0) { 3461ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson // We should never be here... 3471ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson Log.v(TAG, "Will not show notification for timer already expired."); 3481ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson return null; 3491ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 3501ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 3511ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson long hundreds, seconds, minutes, hours; 3521ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson seconds = timeLeft / 1000; 3531ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson minutes = seconds / 60; 3541ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson seconds = seconds - minutes * 60; 3551ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson hours = minutes / 60; 3561ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson minutes = minutes - hours * 60; 3571ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (hours > 99) { 3581ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson hours = 0; 3591ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 3601ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 3611ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson String hourSeq = (hours == 0) ? "" : 3621ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson ( (hours == 1) ? context.getString(R.string.hour) : 3631ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson context.getString(R.string.hours, Long.toString(hours)) ); 3641ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson String minSeq = (minutes == 0) ? "" : 3651ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson ( (minutes == 1) ? context.getString(R.string.minute) : 3661ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson context.getString(R.string.minutes, Long.toString(minutes)) ); 3671ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 3681ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson boolean dispHour = hours > 0; 3691ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson boolean dispMinute = minutes > 0; 3701ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson int index = (dispHour ? 1 : 0) | (dispMinute ? 2 : 0); 3711ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson String[] formats = context.getResources().getStringArray(R.array.timer_notifications); 3721ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson return String.format(formats[index], hourSeq, minSeq); 3731ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 3741ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 3751ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson private TimerObj getNextRunningTimer( 3761ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson ArrayList<TimerObj> timers, boolean requireNextUpdate, long now) { 3771ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson long nextTimesup = Long.MAX_VALUE; 3781ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson boolean nextTimerFound = false; 3791ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson Iterator<TimerObj> i = timers.iterator(); 3801ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson TimerObj t = null; 3811ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson while(i.hasNext()) { 3821ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson TimerObj tmp = i.next(); 3831ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (tmp.mState == TimerObj.STATE_RUNNING) { 3841ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson long timesupTime = tmp.getTimesupTime(); 3851ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson long timeLeft = timesupTime - now; 3861ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (timesupTime < nextTimesup && (!requireNextUpdate || timeLeft > 60) ) { 3871ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson nextTimesup = timesupTime; 3881ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson nextTimerFound = true; 3891ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson t = tmp; 3901ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 3911ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 3921ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 3931ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson if (nextTimerFound) { 3941ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson return t; 3951ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } else { 3961ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson return null; 3971ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 3981ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 3991ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson 4001ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson private void cancelInUseNotification(final Context context) { 4011ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson NotificationManager notificationManager = 4021ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 4031ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson notificationManager.cancel(IN_USE_NOTIFICATION_ID); 4041ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson } 405d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 406d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas private void showTimesUpNotification(final Context context) { 407d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas for (TimerObj timerObj : Timers.timersInTimesUp(mTimers) ) { 408d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas showTimesUpNotification(context, timerObj); 409d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 410d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 411d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 412d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas private void showTimesUpNotification(final Context context, TimerObj timerObj) { 413d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Content Intent. When clicked will show the timer full screen 414d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas PendingIntent contentIntent = PendingIntent.getActivity(context, timerObj.mTimerId, 415d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas new Intent(context, TimerAlertFullScreen.class).putExtra( 416d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas Timers.TIMER_INTENT_EXTRA, timerObj.mTimerId), 417d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas PendingIntent.FLAG_UPDATE_CURRENT); 418d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 419d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Add one minute action button 420d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas PendingIntent addOneMinuteAction = PendingIntent.getBroadcast(context, timerObj.mTimerId, 421d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas new Intent(Timers.NOTIF_TIMES_UP_PLUS_ONE) 422d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .putExtra(Timers.TIMER_INTENT_EXTRA, timerObj.mTimerId), 423d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas PendingIntent.FLAG_UPDATE_CURRENT); 424d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 425d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Add stop/done action button 426d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas PendingIntent stopAction = PendingIntent.getBroadcast(context, timerObj.mTimerId, 427d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas new Intent(Timers.NOTIF_TIMES_UP_STOP) 428d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .putExtra(Timers.TIMER_INTENT_EXTRA, timerObj.mTimerId), 429d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas PendingIntent.FLAG_UPDATE_CURRENT); 430d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 431d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Notification creation 432d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas Notification notification = new Notification.Builder(context) 433d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .setContentIntent(contentIntent) 434d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .addAction(R.drawable.ic_menu_add, 435d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas context.getResources().getString(R.string.timer_plus_1_min), 436d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas addOneMinuteAction) 437d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .addAction( 438d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas timerObj.getDeleteAfterUse() 439d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas ? android.R.drawable.ic_menu_close_clear_cancel 440d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas : R.drawable.ic_stop_normal, 441d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas timerObj.getDeleteAfterUse() 442d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas ? context.getResources().getString(R.string.timer_done) 443d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas : context.getResources().getString(R.string.timer_stop), 444d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas stopAction) 445d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .setContentTitle(timerObj.getLabelOrDefault(context)) 446d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .setContentText(context.getResources().getString(R.string.timer_times_up)) 447d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .setSmallIcon(R.drawable.stat_notify_timer) 448d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .setOngoing(true) 449d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .setAutoCancel(false) 450d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .setPriority(Notification.PRIORITY_MAX) 451d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .setDefaults(Notification.DEFAULT_LIGHTS) 452d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .setWhen(0) 453d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas .build(); 454d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 455d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // Send the notification using the timer's id to identify the 456d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas // correct notification 457d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).notify( 458d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas timerObj.mTimerId, notification); 459d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas if (Timers.LOGGING) { 460d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas Log.v(TAG, "Setting times-up notification for " 461d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas + timerObj.getLabelOrDefault(context) + " #" + timerObj.mTimerId); 462d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 463d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 464d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 465d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas private void cancelTimesUpNotification(final Context context) { 466d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas for (TimerObj timerObj : Timers.timersInTimesUp(mTimers) ) { 467d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas cancelTimesUpNotification(context, timerObj); 468d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 469d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 470d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas 471d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas private void cancelTimesUpNotification(final Context context, TimerObj timerObj) { 472d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas NotificationManager notificationManager = 473d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 474d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas notificationManager.cancel(timerObj.mTimerId); 475d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas if (Timers.LOGGING) { 476d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas Log.v(TAG, "Canceling times-up notification for " 477d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas + timerObj.getLabelOrDefault(context) + " #" + timerObj.mTimerId); 478d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 479d5202b46aaf96b1d217cedba645f28aa1fee04c6Robyn Coultas } 480feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas} 481