2 * Copyright (C) 2007 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.deskclock;
19 import android.app.KeyguardManager;
20 import android.app.Notification;
21 import android.app.NotificationManager;
22 import android.app.PendingIntent;
23 import android.content.ContentUris;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.BroadcastReceiver;
27 import android.database.Cursor;
28 import android.os.Parcel;
30 import java.text.SimpleDateFormat;
31 import java.util.Date;
34 * Glue class: connects AlarmAlert IntentReceiver to AlarmAlert
35 * activity. Passes through Alarm ID.
37 public class AlarmReceiver extends BroadcastReceiver {
39 /** If the alarm is older than STALE_WINDOW seconds, ignore. It
40 is probably the result of a time or timezone change */
41 private final static int STALE_WINDOW = 60 * 30;
44 public void onReceive(Context context, Intent intent) {
45 // Take care of the easy intents first.
46 if (Alarms.CLEAR_NOTIFICATION.equals(intent.getAction())) {
47 // If this is the "Clear All Notifications" intent, stop the alarm
48 // service and return.
49 context.stopService(new Intent(Alarms.ALARM_ALERT_ACTION));
51 } else if (Alarms.ALARM_KILLED.equals(intent.getAction())) {
52 // The alarm has been killed, update the notification
53 updateNotification(context, (Alarm)
54 intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA),
55 intent.getIntExtra(Alarms.ALARM_KILLED_TIMEOUT, -1));
57 } else if (Alarms.CANCEL_SNOOZE.equals(intent.getAction())) {
58 Alarms.saveSnoozeAlert(context, -1, -1);
63 // Grab the alarm from the intent. Since the remote AlarmManagerService
64 // fills in the Intent to add some extra data, it must unparcel the
65 // Alarm object. It throws a ClassNotFoundException when unparcelling.
66 // To avoid this, do the marshalling ourselves.
67 final byte[] data = intent.getByteArrayExtra(Alarms.ALARM_RAW_DATA);
69 Parcel in = Parcel.obtain();
70 in.unmarshall(data, 0, data.length);
71 in.setDataPosition(0);
72 alarm = Alarm.CREATOR.createFromParcel(in);
76 Log.v("AlarmReceiver failed to parse the alarm from the intent");
80 // Intentionally verbose: always log the alarm time to provide useful
81 // information in bug reports.
82 long now = System.currentTimeMillis();
83 SimpleDateFormat format =
84 new SimpleDateFormat("HH:mm:ss.SSS aaa");
85 Log.v("AlarmReceiver.onReceive() id " + alarm.id + " setFor "
86 + format.format(new Date(alarm.time)));
88 if (now > alarm.time + STALE_WINDOW * 1000) {
90 Log.v("AlarmReceiver ignoring stale alarm");
95 // Maintain a cpu wake lock until the AlarmAlert and AlarmKlaxon can
97 AlarmAlertWakeLock.acquireCpuWakeLock(context);
99 /* Close dialogs and window shade */
100 Intent closeDialogs = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
101 context.sendBroadcast(closeDialogs);
103 // Decide which activity to start based on the state of the keyguard.
104 Class c = AlarmAlert.class;
105 KeyguardManager km = (KeyguardManager) context.getSystemService(
106 Context.KEYGUARD_SERVICE);
107 if (km.inKeyguardRestrictedInputMode()) {
108 // Use the full screen activity for security.
109 c = AlarmAlertFullScreen.class;
112 /* launch UI, explicitly stating that this is not due to user action
113 * so that the current app's notification management is not disturbed */
114 Intent alarmAlert = new Intent(context, c);
115 alarmAlert.putExtra(Alarms.ALARM_INTENT_EXTRA, alarm);
116 alarmAlert.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
117 | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
118 context.startActivity(alarmAlert);
120 // Disable the snooze alert if this alarm is the snooze.
121 Alarms.disableSnoozeAlert(context, alarm.id);
122 // Disable this alarm if it does not repeat.
123 if (!alarm.daysOfWeek.isRepeatSet()) {
124 Alarms.enableAlarm(context, alarm.id, false);
126 // Enable the next alert if there is one. The above call to
127 // enableAlarm will call setNextAlert so avoid calling it twice.
128 Alarms.setNextAlert(context);
131 // Play the alarm alert and vibrate the device.
132 Intent playAlarm = new Intent(Alarms.ALARM_ALERT_ACTION);
133 playAlarm.putExtra(Alarms.ALARM_INTENT_EXTRA, alarm);
134 context.startService(playAlarm);
136 // Trigger a notification that, when clicked, will show the alarm alert
137 // dialog. No need to check for fullscreen since this will always be
138 // launched from a user action.
139 Intent notify = new Intent(context, AlarmAlert.class);
140 notify.putExtra(Alarms.ALARM_INTENT_EXTRA, alarm);
141 PendingIntent pendingNotify = PendingIntent.getActivity(context,
142 alarm.id, notify, 0);
144 // Use the alarm's label or the default label as the ticker text and
145 // main text of the notification.
146 String label = alarm.getLabelOrDefault(context);
147 Notification n = new Notification(R.drawable.stat_notify_alarm,
149 n.setLatestEventInfo(context, label,
150 context.getString(R.string.alarm_notify_text),
152 n.flags |= Notification.FLAG_SHOW_LIGHTS;
153 n.ledARGB = 0xFF00FF00;
157 // Set the deleteIntent for when the user clicks "Clear All
159 Intent clearAll = new Intent(context, AlarmReceiver.class);
160 clearAll.setAction(Alarms.CLEAR_NOTIFICATION);
161 n.deleteIntent = PendingIntent.getBroadcast(context, 0, clearAll, 0);
163 // Send the notification using the alarm id to easily identify the
164 // correct notification.
165 NotificationManager nm = getNotificationManager(context);
166 nm.notify(alarm.id, n);
169 private NotificationManager getNotificationManager(Context context) {
170 return (NotificationManager)
171 context.getSystemService(Context.NOTIFICATION_SERVICE);
174 private void updateNotification(Context context, Alarm alarm, int timeout) {
175 NotificationManager nm = getNotificationManager(context);
177 // If the alarm is null, just cancel the notification.
180 Log.v("Cannot update notification for killer callback");
185 // Launch SetAlarm when clicked.
186 Intent viewAlarm = new Intent(context, SetAlarm.class);
187 viewAlarm.putExtra(Alarms.ALARM_ID, alarm.id);
188 PendingIntent intent =
189 PendingIntent.getActivity(context, alarm.id, viewAlarm, 0);
191 // Update the notification to indicate that the alert has been
193 String label = alarm.getLabelOrDefault(context);
194 Notification n = new Notification(R.drawable.stat_notify_alarm,
196 n.setLatestEventInfo(context, label,
197 context.getString(R.string.alarm_alert_alert_silenced, timeout),
199 n.flags |= Notification.FLAG_AUTO_CANCEL;
200 nm.notify(alarm.id, n);