OSDN Git Service

android-2.1_r1 snapshot
[android-x86/packages-apps-DeskClock.git] / src / com / android / deskclock / AlarmReceiver.java
diff --git a/src/com/android/deskclock/AlarmReceiver.java b/src/com/android/deskclock/AlarmReceiver.java
new file mode 100644 (file)
index 0000000..92f929a
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.deskclock;
+
+import android.app.KeyguardManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.database.Cursor;
+import android.os.Parcel;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Glue class: connects AlarmAlert IntentReceiver to AlarmAlert
+ * activity.  Passes through Alarm ID.
+ */
+public class AlarmReceiver extends BroadcastReceiver {
+
+    /** If the alarm is older than STALE_WINDOW seconds, ignore.  It
+        is probably the result of a time or timezone change */
+    private final static int STALE_WINDOW = 60 * 30;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (Alarms.ALARM_KILLED.equals(intent.getAction())) {
+            // The alarm has been killed, update the notification
+            updateNotification(context, (Alarm)
+                    intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA),
+                    intent.getIntExtra(Alarms.ALARM_KILLED_TIMEOUT, -1));
+            return;
+        } else if (Alarms.CANCEL_SNOOZE.equals(intent.getAction())) {
+            Alarms.saveSnoozeAlert(context, -1, -1);
+            return;
+        }
+
+        Alarm alarm = null;
+        // Grab the alarm from the intent. Since the remote AlarmManagerService
+        // fills in the Intent to add some extra data, it must unparcel the
+        // Alarm object. It throws a ClassNotFoundException when unparcelling.
+        // To avoid this, do the marshalling ourselves.
+        final byte[] data = intent.getByteArrayExtra(Alarms.ALARM_RAW_DATA);
+        if (data != null) {
+            Parcel in = Parcel.obtain();
+            in.unmarshall(data, 0, data.length);
+            in.setDataPosition(0);
+            alarm = Alarm.CREATOR.createFromParcel(in);
+        }
+
+        if (alarm == null) {
+            Log.v("AlarmReceiver failed to parse the alarm from the intent");
+            return;
+        }
+
+        // Intentionally verbose: always log the alarm time to provide useful
+        // information in bug reports.
+        long now = System.currentTimeMillis();
+        SimpleDateFormat format =
+                new SimpleDateFormat("HH:mm:ss.SSS aaa");
+        Log.v("AlarmReceiver.onReceive() id " + alarm.id + " setFor "
+                + format.format(new Date(alarm.time)));
+
+        if (now > alarm.time + STALE_WINDOW * 1000) {
+            if (Log.LOGV) {
+                Log.v("AlarmReceiver ignoring stale alarm");
+            }
+            return;
+        }
+
+        // Maintain a cpu wake lock until the AlarmAlert and AlarmKlaxon can
+        // pick it up.
+        AlarmAlertWakeLock.acquireCpuWakeLock(context);
+
+        /* Close dialogs and window shade */
+        Intent closeDialogs = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+        context.sendBroadcast(closeDialogs);
+
+        // Decide which activity to start based on the state of the keyguard.
+        Class c = AlarmAlert.class;
+        KeyguardManager km = (KeyguardManager) context.getSystemService(
+                Context.KEYGUARD_SERVICE);
+        if (km.inKeyguardRestrictedInputMode()) {
+            // Use the full screen activity for security.
+            c = AlarmAlertFullScreen.class;
+        }
+
+        /* launch UI, explicitly stating that this is not due to user action
+         * so that the current app's notification management is not disturbed */
+        Intent alarmAlert = new Intent(context, c);
+        alarmAlert.putExtra(Alarms.ALARM_INTENT_EXTRA, alarm);
+        alarmAlert.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+        context.startActivity(alarmAlert);
+
+        // Disable the snooze alert if this alarm is the snooze.
+        Alarms.disableSnoozeAlert(context, alarm.id);
+        // Disable this alarm if it does not repeat.
+        if (!alarm.daysOfWeek.isRepeatSet()) {
+            Alarms.enableAlarm(context, alarm.id, false);
+        } else {
+            // Enable the next alert if there is one. The above call to
+            // enableAlarm will call setNextAlert so avoid calling it twice.
+            Alarms.setNextAlert(context);
+        }
+
+        // Play the alarm alert and vibrate the device.
+        Intent playAlarm = new Intent(Alarms.ALARM_ALERT_ACTION);
+        playAlarm.putExtra(Alarms.ALARM_INTENT_EXTRA, alarm);
+        context.startService(playAlarm);
+
+        // Trigger a notification that, when clicked, will show the alarm alert
+        // dialog. No need to check for fullscreen since this will always be
+        // launched from a user action.
+        Intent notify = new Intent(context, AlarmAlert.class);
+        notify.putExtra(Alarms.ALARM_INTENT_EXTRA, alarm);
+        PendingIntent pendingNotify = PendingIntent.getActivity(context,
+                alarm.id, notify, 0);
+
+        // Use the alarm's label or the default label as the ticker text and
+        // main text of the notification.
+        String label = alarm.getLabelOrDefault(context);
+        Notification n = new Notification(R.drawable.stat_notify_alarm,
+                label, alarm.time);
+        n.setLatestEventInfo(context, label,
+                context.getString(R.string.alarm_notify_text),
+                pendingNotify);
+        n.flags |= Notification.FLAG_SHOW_LIGHTS
+                | Notification.FLAG_ONGOING_EVENT;
+        n.ledARGB = 0xFF00FF00;
+        n.ledOnMS = 500;
+        n.ledOffMS = 500;
+
+        // Send the notification using the alarm id to easily identify the
+        // correct notification.
+        NotificationManager nm = getNotificationManager(context);
+        nm.notify(alarm.id, n);
+    }
+
+    private NotificationManager getNotificationManager(Context context) {
+        return (NotificationManager)
+                context.getSystemService(Context.NOTIFICATION_SERVICE);
+    }
+
+    private void updateNotification(Context context, Alarm alarm, int timeout) {
+        NotificationManager nm = getNotificationManager(context);
+
+        // If the alarm is null, just cancel the notification.
+        if (alarm == null) {
+            if (Log.LOGV) {
+                Log.v("Cannot update notification for killer callback");
+            }
+            return;
+        }
+
+        // Launch SetAlarm when clicked.
+        Intent viewAlarm = new Intent(context, SetAlarm.class);
+        viewAlarm.putExtra(Alarms.ALARM_ID, alarm.id);
+        PendingIntent intent =
+                PendingIntent.getActivity(context, alarm.id, viewAlarm, 0);
+
+        // Update the notification to indicate that the alert has been
+        // silenced.
+        String label = alarm.getLabelOrDefault(context);
+        Notification n = new Notification(R.drawable.stat_notify_alarm,
+                label, alarm.time);
+        n.setLatestEventInfo(context, label,
+                context.getString(R.string.alarm_alert_alert_silenced, timeout),
+                intent);
+        n.flags |= Notification.FLAG_AUTO_CANCEL;
+        // We have to cancel the original notification since it is in the
+        // ongoing section and we want the "killed" notification to be a plain
+        // notification.
+        nm.cancel(alarm.id);
+        nm.notify(alarm.id, n);
+    }
+}