TimerReceiver.java revision feabb7a335fb8c7644783d2dca53b864009054d8
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
4164575cfee07cf0ec38f65a12055692f542e78e70Sam Blitzstein    private static final int IN_USE_NOTIFICATION_ID = 2;
421ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
43871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson    ArrayList<TimerObj> mTimers;
44871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson
45871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson    @Override
4663211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson    public void onReceive(final Context context, final Intent intent) {
471ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        int timer;
481ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        String actionType = intent.getAction();
4963211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson
501ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        // Get the updated timers data.
511ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        if (mTimers == null) {
521ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            mTimers = new ArrayList<TimerObj> ();
531ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        }
541ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
551ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        TimerObj.getTimersFromSharedPrefs(prefs, mTimers);
5663211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson
5763211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson
581ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        if (intent.hasExtra(Timers.TIMER_INTENT_EXTRA)) {
591ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            // Get the alarm out of the Intent
601ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            timer = intent.getIntExtra(Timers.TIMER_INTENT_EXTRA, -1);
611ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            if (timer == -1) {
621ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                Log.d(TAG, " got intent without Timer data: "+actionType);
631ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            }
641ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        } else if (Timers.NOTIF_IN_USE_SHOW.equals(actionType)){
651ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            showInUseNotification(context);
661ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            return;
671ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        } else if (Timers.NOTIF_IN_USE_CANCEL.equals(actionType)) {
681ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            cancelInUseNotification(context);
691ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            return;
701ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        } else {
711ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            // No data to work with, do nothing
721ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            Log.d(TAG, " got intent without Timer data");
731ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            return;
741ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        }
751ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
761ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        TimerObj t = Timers.findTimer(mTimers, timer);
7763211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson
781ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        if (intent.getBooleanExtra(Timers.UPDATE_NOTIFICATION, false)) {
791ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            if (Timers.TIMER_STOP.equals(actionType)) {
801ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                if (t == null) {
811ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                    Log.d(TAG, "timer not found in list - can't stop it.");
821ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                    return;
831ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                }
841ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                t.mState = TimerObj.STATE_DONE;
851ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                t.writeToSharedPref(prefs);
861ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                SharedPreferences.Editor editor = prefs.edit();
871ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                editor.putBoolean(Timers.FROM_NOTIFICATION, true);
88a65d3eb00d543735305221f86ce630c8650e65e2Isaac Katzenelson                editor.putLong(Timers.NOTIF_TIME, Utils.getTimeNow());
891ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                editor.putInt(Timers.NOTIF_ID, timer);
901ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                editor.apply();
91871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson
921ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                stopRingtoneIfNoTimesup(context);
93871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson
941ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                Intent activityIntent = new Intent(context, DeskClock.class);
951ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
961ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                activityIntent.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX);
971ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                context.startActivity(activityIntent);
981ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            }
991ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson             return;
1001ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        }
1011ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
1021ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        if (Timers.TIMES_UP.equals(actionType)) {
103871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson            // Find the timer (if it doesn't exists, it was probably deleted).
104871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson            if (t == null) {
105871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson                Log.d(TAG, " timer not found in list - do nothing");
106871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson                return;
107871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson            }
108871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson
109871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson            t.mState = TimerObj.STATE_TIMESUP;
110871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson            t.writeToSharedPref(prefs);
111647353dd437f8c075aa96f3b98efb84442e8b7bfRobyn Coultas            // Play ringtone by using TimerRingService service with a default alarm.
112871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson            Log.d(TAG, "playing ringtone");
113871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson            Intent si = new Intent();
1143f99104b4dc4df9a774ff7433084b8e483d88744Isaac Katzenelson            si.setClass(context, TimerRingService.class);
115871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson            context.startService(si);
116871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson
117feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas            // Update the in-use notification
118a65d3eb00d543735305221f86ce630c8650e65e2Isaac Katzenelson            if (getNextRunningTimer(mTimers, false, Utils.getTimeNow()) == null) {
1191ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                // Found no running timers.
1201ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                cancelInUseNotification(context);
121feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas            } else {
122feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas                showInUseNotification(context);
123871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson            }
1241ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
125feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas            // Start the TimerAlertFullScreen activity.
126feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas            Intent timersAlert = new Intent(context, TimerAlertFullScreen.class);
127feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas            timersAlert.setFlags(
128feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
129feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas            context.startActivity(timersAlert);
1301ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        } else if (Timers.TIMER_RESET.equals(actionType)
1311ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                || Timers.DELETE_TIMER.equals(actionType)
1321ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                || Timers.TIMER_DONE.equals(actionType)) {
133feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas            // Stop Ringtone if all timers are not in times-up status
1341ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            stopRingtoneIfNoTimesup(context);
135871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson        }
136871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson        // Update the next "Times up" alarm
1371ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        updateNextTimesup(context);
1381ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    }
1391ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
1401ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    private void stopRingtoneIfNoTimesup(final Context context) {
1411ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        if (Timers.findExpiredTimer(mTimers) == null) {
1421ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            // Stop ringtone
1431ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            Log.d(TAG, "stopping ringtone");
1441ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            Intent si = new Intent();
1451ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            si.setClass(context, TimerRingService.class);
1461ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            context.stopService(si);
1471ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        }
14863211efa1565b5dc171938740ba183302d0e46f3Isaac Katzenelson    }
149871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson
150871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson    // Scan all timers and find the one that will expire next.
151871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson    // Tell AlarmManager to send a "Time's up" message to this receiver when this timer expires.
152871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson    // If no timer exists, clear "time's up" message.
153871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson    private void updateNextTimesup(Context context) {
154a65d3eb00d543735305221f86ce630c8650e65e2Isaac Katzenelson        TimerObj t = getNextRunningTimer(mTimers, false, Utils.getTimeNow());
1551ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        long nextTimesup = (t == null) ? -1 : t.getTimesupTime();
1561ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        int timerId = (t == null) ? -1 : t.mTimerId;
157871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson
158871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson        Intent intent = new Intent();
159871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson        intent.setAction(Timers.TIMES_UP);
160871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson        intent.setClass(context, TimerReceiver.class);
1611ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        if (!mTimers.isEmpty()) {
1621ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            intent.putExtra(Timers.TIMER_INTENT_EXTRA, timerId);
163871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson        }
164871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson        AlarmManager mngr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
165871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson        PendingIntent p = PendingIntent.getBroadcast(context,
166871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson                0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
1671ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        if (t != null) {
168a65d3eb00d543735305221f86ce630c8650e65e2Isaac Katzenelson            mngr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTimesup, p);
169871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson            Log.d(TAG,"Setting times up to " + nextTimesup);
170871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson        } else {
171871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson            Log.d(TAG,"canceling times up");
172871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson            mngr.cancel(p);
173871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson        }
174871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson    }
175871d7d00b561dc0e100c5ac7f79542d20ae9578dIsaac Katzenelson
1761ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    private void showInUseNotification(final Context context) {
1771ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
1781ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        boolean appOpen = prefs.getBoolean(Timers.NOTIF_APP_OPEN, false);
1791ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        ArrayList<TimerObj> timersInUse = Timers.timersInUse(mTimers);
1801ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        int numTimersInUse = timersInUse.size();
1811ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
1821ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        if (appOpen || numTimersInUse == 0) {
1831ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            return;
1841ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        }
1851ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
1861ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        String title, contentText;
1871ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        Long nextBroadcastTime = null;
188a65d3eb00d543735305221f86ce630c8650e65e2Isaac Katzenelson        long now = Utils.getTimeNow();
1891ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        if (timersInUse.size() == 1) {
1901ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            TimerObj timer = timersInUse.get(0);
1910f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein            String label = timer.mLabel.equals("") ?
1920f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein                    context.getString(R.string.timer_notification_label) : timer.mLabel;
1931ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            title = timer.isTicking() ? label : context.getString(R.string.timer_stopped);
1941ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            long timeLeft = timer.isTicking() ? timer.getTimesupTime() - now : timer.mTimeLeft;
1951ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            contentText = buildTimeRemaining(context, timeLeft);
1961ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            if (timeLeft > 60) {
1971ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                nextBroadcastTime = getBroadcastTime(now, timeLeft);
1981ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            }
1991ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        } else {
2001ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            TimerObj timer = getNextRunningTimer(timersInUse, false, now);
2011ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            if (timer == null) {
2021ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                // No running timers.
2031ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                title = String.format(
2041ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                        context.getString(R.string.timers_stopped), numTimersInUse);
2051ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                contentText = context.getString(R.string.all_timers_stopped_notif);
2061ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            } else {
2071ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                // We have at least one timer running and other timers stopped.
2081ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                title = String.format(
2091ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                        context.getString(R.string.timers_in_use), numTimersInUse);
2101ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                long completionTime = timer.getTimesupTime();
2111ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                long timeLeft = completionTime - now;
2121ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                contentText = String.format(context.getString(R.string.next_timer_notif),
2131ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                        buildTimeRemaining(context, timeLeft));
2141ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                if (timeLeft <= 60) {
2151ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                    TimerObj timerWithUpdate = getNextRunningTimer(timersInUse, true, now);
2161ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                    if (timerWithUpdate != null) {
2171ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                        completionTime = timerWithUpdate.getTimesupTime();
2181ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                        timeLeft = completionTime - now;
2191ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                        nextBroadcastTime = getBroadcastTime(now, timeLeft);
2201ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                    }
2211ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                } else {
2221ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                    nextBroadcastTime = getBroadcastTime(now, timeLeft);
2231ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                }
2241ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            }
2251ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        }
2261ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        showCollapsedNotificationWithNext(context, title, contentText, nextBroadcastTime);
2271ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    }
2281ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
2291ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    private long getBroadcastTime(long now, long timeUntilBroadcast) {
2301ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        long seconds = timeUntilBroadcast / 1000;
2311ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        seconds = seconds - ( (seconds / 60) * 60 );
2321ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        return now + (seconds * 1000);
2331ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    }
2341ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
2350f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein    /** Public and static to allow timer fragment to update notification with new label. **/
2360f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein    public static void showExpiredAlarmNotification(Context context, TimerObj t) {
2370f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein        Intent broadcastIntent = new Intent();
2380f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein        broadcastIntent.putExtra(Timers.TIMER_INTENT_EXTRA, t.mTimerId);
2390f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein        broadcastIntent.setAction(Timers.TIMER_STOP);
2400f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein        broadcastIntent.putExtra(Timers.UPDATE_NOTIFICATION, true);
2410f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein        PendingIntent pendingBroadcastIntent = PendingIntent.getBroadcast(
2420f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein                context, 0, broadcastIntent, 0);
2430f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein        String label = t.mLabel.equals("") ? context.getString(R.string.timer_notification_label) :
2440f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein            t.mLabel;
2450f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein        String contentText = context.getString(R.string.timer_times_up);
2460f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein        showCollapsedNotification(context, label, contentText, Notification.PRIORITY_MAX,
2470f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein                pendingBroadcastIntent, t.mTimerId, true);
2480f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein    }
2490f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein
2501ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    private void showCollapsedNotificationWithNext(
2511ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            final Context context, String title, String text, Long nextBroadcastTime) {
2521ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        Intent activityIntent = new Intent(context, DeskClock.class);
2531ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2541ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        activityIntent.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX);
2551ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        PendingIntent pendingActivityIntent = PendingIntent.getActivity(context, 0, activityIntent,
2561ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
2571ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        showCollapsedNotification(context, title, text, Notification.PRIORITY_HIGH,
25806077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein                pendingActivityIntent, IN_USE_NOTIFICATION_ID, false);
2591ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
2601ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        if (nextBroadcastTime == null) {
2611ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            return;
2621ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        }
2631ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        Intent nextBroadcast = new Intent();
2641ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        nextBroadcast.setAction(Timers.NOTIF_IN_USE_SHOW);
2651ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        PendingIntent pendingNextBroadcast =
2661ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                PendingIntent.getBroadcast(context, 0, nextBroadcast, 0);
2671ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        AlarmManager alarmManager =
2681ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
269a65d3eb00d543735305221f86ce630c8650e65e2Isaac Katzenelson        alarmManager.set(AlarmManager.ELAPSED_REALTIME, nextBroadcastTime, pendingNextBroadcast);
2701ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    }
2711ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
2720f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein    private static void showCollapsedNotification(final Context context, String title, String text,
27306077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein            int priority, PendingIntent pendingIntent, int notificationId, boolean showTicker) {
27406077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein        Notification.Builder builder = new Notification.Builder(context)
2751ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        .setAutoCancel(false)
2761ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        .setContentTitle(title)
2771ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        .setContentText(text)
2781ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        .setDeleteIntent(pendingIntent)
2791ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        .setOngoing(true)
2801ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        .setPriority(priority)
2811ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        .setShowWhen(false)
28206077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein        .setSmallIcon(R.drawable.stat_notify_timer);
28306077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein        if (showTicker) {
28406077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein            builder.setTicker(text);
28506077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein        }
28606077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein
28706077d25a16684bc4d26c1ee0c48db556983c86cSam Blitzstein        Notification notification = builder.build();
2881ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        notification.contentIntent = pendingIntent;
2891ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        NotificationManager notificationManager =
2901ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
2911ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        notificationManager.notify(notificationId, notification);
2921ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    }
2931ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
2941ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    private String buildTimeRemaining(Context context, long timeLeft) {
2951ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        if (timeLeft < 0) {
2961ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            // We should never be here...
2971ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            Log.v(TAG, "Will not show notification for timer already expired.");
2981ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            return null;
2991ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        }
3001ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
3011ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        long hundreds, seconds, minutes, hours;
3021ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        seconds = timeLeft / 1000;
3031ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        minutes = seconds / 60;
3041ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        seconds = seconds - minutes * 60;
3051ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        hours = minutes / 60;
3061ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        minutes = minutes - hours * 60;
3071ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        if (hours > 99) {
3081ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            hours = 0;
3091ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        }
3101ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
3111ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        String hourSeq = (hours == 0) ? "" :
3121ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            ( (hours == 1) ? context.getString(R.string.hour) :
3131ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                context.getString(R.string.hours, Long.toString(hours)) );
3141ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        String minSeq = (minutes == 0) ? "" :
3151ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            ( (minutes == 1) ? context.getString(R.string.minute) :
3161ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                context.getString(R.string.minutes, Long.toString(minutes)) );
3171ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
3181ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        boolean dispHour = hours > 0;
3191ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        boolean dispMinute = minutes > 0;
3201ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        int index = (dispHour ? 1 : 0) | (dispMinute ? 2 : 0);
3211ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        String[] formats = context.getResources().getStringArray(R.array.timer_notifications);
3221ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        return String.format(formats[index], hourSeq, minSeq);
3231ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    }
3241ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
3251ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    private TimerObj getNextRunningTimer(
3261ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            ArrayList<TimerObj> timers, boolean requireNextUpdate, long now) {
3271ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        long nextTimesup = Long.MAX_VALUE;
3281ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        boolean nextTimerFound = false;
3291ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        Iterator<TimerObj> i = timers.iterator();
3301ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        TimerObj t = null;
3311ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        while(i.hasNext()) {
3321ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            TimerObj tmp = i.next();
3331ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            if (tmp.mState == TimerObj.STATE_RUNNING) {
3341ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                long timesupTime = tmp.getTimesupTime();
3351ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                long timeLeft = timesupTime - now;
3361ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                if (timesupTime < nextTimesup && (!requireNextUpdate || timeLeft > 60) ) {
3371ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                    nextTimesup = timesupTime;
3381ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                    nextTimerFound = true;
3391ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                    t = tmp;
3401ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                }
3411ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            }
3421ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        }
3431ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        if (nextTimerFound) {
3441ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            return t;
3451ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        } else {
3461ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson            return null;
3471ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        }
3481ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    }
3491ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson
3501ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    private void cancelInUseNotification(final Context context) {
3511ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        NotificationManager notificationManager =
3521ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
3531ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson        notificationManager.cancel(IN_USE_NOTIFICATION_ID);
3541ce69abc1f8bea2d7903f24ee32fec42cee83634Itzhak Katzenelson    }
355feabb7a335fb8c7644783d2dca53b864009054d8Robyn Coultas}
356