OSDN Git Service

auto import from //branches/cupcake_rel/...@140373
[android-x86/packages-apps-DeskClock.git] / src / com / android / alarmclock / AlarmAlert.java
index 31da676..6a9a718 100644 (file)
@@ -18,21 +18,20 @@ package com.android.alarmclock;
 
 import android.app.Activity;
 import android.app.KeyguardManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
-import android.media.AudioManager;
-import android.net.Uri;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
 import android.os.Bundle;
-import android.os.Handler;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.LayoutInflater;
+import android.view.WindowManager;
 import android.widget.Button;
 import android.widget.Toast;
+import android.widget.TextView;
 
 import java.util.Calendar;
 
@@ -40,61 +39,60 @@ import java.util.Calendar;
  * Alarm Clock alarm alert: pops visible indicator and plays alarm
  * tone
  */
-public class AlarmAlert extends Activity implements Alarms.AlarmSettings {
+public class AlarmAlert extends Activity {
 
-    private static long[] sVibratePattern = new long[] { 500, 500 };
-
-    private NotificationManager mNotificationManager;
-
-    private final static int AUDIO_ALERT_NOTIFICATION_ID = 0;
-    /** Play alarm up to 5 minutes before silencing */
-    private final static int ALARM_TIMEOUT_SECONDS = 300;
-    private final static int SNOOZE_MINUTES = 10;
+    private static final int SNOOZE_MINUTES = 10;
+    private static final int UNKNOWN = 0;
+    private static final int SNOOZE = 1;
+    private static final int DISMISS = 2;
+    private static final int KILLED = 3;
 
     private KeyguardManager mKeyguardManager;
-    private KeyguardManager.KeyguardLock mKeyguardLock = null;
-    private Handler mTimeout;
+    private KeyguardManager.KeyguardLock mKeyguardLock;
     private Button mSnoozeButton;
+    private int mState = UNKNOWN;
 
+    private AlarmKlaxon mKlaxon;
     private int mAlarmId;
-    private Alarms.DaysOfWeek mDaysOfWeek;
-    private String mAlert;
-    private boolean mVibrate;
-    private boolean mSnoozed;
-    private boolean mCleanupCalled = false;
-
-    public void reportAlarm(
-            int idx, boolean enabled, int hour, int minutes,
-            Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String message,
-            String alert) {
-        if (Log.LOGV) Log.v("AlarmAlert.reportAlarm: " + idx + " " + hour +
-                            " " + minutes + " dow " + daysOfWeek);
-        mAlert = alert;
-        mDaysOfWeek = daysOfWeek;
-        mVibrate = vibrate;
-    }
+    private String mLabel;
 
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        setContentView(R.layout.alarm_alert);
+
+        // Maintain a lock during the playback of the alarm. This lock may have
+        // already been acquired in AlarmReceiver. If the process was killed,
+        // the global wake lock is gone. Acquire again just to be sure.
+        AlarmAlertWakeLock.acquire(this);
+
+        /* FIXME Intentionally verbose: always log this until we've
+           fully debugged the app failing to start up */
+        Log.v("AlarmAlert.onCreate()");
+
+        // Popup alert over black screen
+        WindowManager.LayoutParams lp = getWindow().getAttributes();
+        lp.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+        lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+        // XXX DO NOT COPY THIS!!!  THIS IS BOGUS!  Making an activity have
+        // a system alert type is completely broken, because the activity
+        // manager will still hide/show it as if it is part of the normal
+        // activity stack.  If this is really what you want and you want it
+        // to work correctly, you should create and show your own custom window.
+        lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+        lp.token = null;
+        getWindow().setAttributes(lp);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
 
         mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
-        mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
 
-        /* set clock face */
-        LayoutInflater mFactory = LayoutInflater.from(this);
-        SharedPreferences settings = getSharedPreferences(AlarmClock.PREFERENCES, 0);
-        int face = settings.getInt(AlarmClock.PREF_CLOCK_FACE, 0);
-        if (face < 0 || face >= AlarmClock.CLOCKS.length) face = 0;
-        View clockLayout = (View)mFactory.inflate(AlarmClock.CLOCKS[face], null);
-        ViewGroup clockView = (ViewGroup)findViewById(R.id.clockView);
-        clockView.addView(clockLayout);
-        if (clockLayout instanceof DigitalClock) {
-            ((DigitalClock)clockLayout).setAnimate();
-        }
+        Intent i = getIntent();
+        mAlarmId = i.getIntExtra(Alarms.ID, -1);
+
+        mKlaxon = new AlarmKlaxon();
+        mKlaxon.postPlay(this, mAlarmId);
 
-        playAlert(getIntent());
+        /* Set the title from the passed in label */
+        setTitleFromIntent(i);
 
         /* allow next alarm to trigger while this activity is
            active */
@@ -102,49 +100,129 @@ public class AlarmAlert extends Activity implements Alarms.AlarmSettings {
         Alarms.disableAlert(AlarmAlert.this, mAlarmId);
         Alarms.setNextAlert(this);
 
+        mKlaxon.setKillerCallback(new AlarmKlaxon.KillerCallback() {
+            public void onKilled() {
+                if (Log.LOGV) Log.v("onKilled()");
+                updateSilencedText();
+
+                /* don't allow snooze */
+                mSnoozeButton.setEnabled(false);
+
+                // Dismiss the alarm but mark the state as killed so if the
+                // config changes, we show the silenced message and disable
+                // snooze.
+                dismiss();
+                mState = KILLED;
+            }
+        });
+
+        updateLayout();
+    }
+
+    private void setTitleFromIntent(Intent i) {
+        mLabel = i.getStringExtra(Alarms.LABEL);
+        if (mLabel == null || mLabel.length() == 0) {
+            mLabel = getString(R.string.default_label);
+        }
+        setTitle(mLabel);
+    }
+
+    private void updateSilencedText() {
+        TextView silenced = (TextView) findViewById(R.id.silencedText);
+        silenced.setText(getString(R.string.alarm_alert_alert_silenced,
+                    AlarmKlaxon.ALARM_TIMEOUT_SECONDS / 60));
+        silenced.setVisibility(View.VISIBLE);
+    }
+
+    private void updateLayout() {
+        setContentView(R.layout.alarm_alert);
+
+        /* set clock face */
+        LayoutInflater mFactory = LayoutInflater.from(this);
+        SharedPreferences settings =
+                getSharedPreferences(AlarmClock.PREFERENCES, 0);
+        int face = settings.getInt(AlarmClock.PREF_CLOCK_FACE, 0);
+        if (face < 0 || face >= AlarmClock.CLOCKS.length) {
+            face = 0;
+        }
+        View clockLayout =
+                (View) mFactory.inflate(AlarmClock.CLOCKS[face], null);
+        ViewGroup clockView = (ViewGroup) findViewById(R.id.clockView);
+        clockView.addView(clockLayout);
+        if (clockLayout instanceof DigitalClock) {
+            ((DigitalClock) clockLayout).setAnimate();
+        }
+
         /* snooze behavior: pop a snooze confirmation view, kick alarm
            manager. */
         mSnoozeButton = (Button) findViewById(R.id.snooze);
         mSnoozeButton.requestFocus();
-        mSnoozeButton.setOnClickListener(new Button.OnClickListener() {
-            public void onClick(View v) {
-                /* If next alarm is set for sooner than the snooze interval,
-                   don't snooze: instead toast user that snooze will not be set */
-                final long snoozeTarget = System.currentTimeMillis() + 1000 * 60 * SNOOZE_MINUTES;
-                long nextAlarm = Alarms.calculateNextAlert(AlarmAlert.this).getAlert();
-                if (nextAlarm < snoozeTarget) {
-                    Calendar c = Calendar.getInstance();
-                    c.setTimeInMillis(nextAlarm);
-                    Toast.makeText(AlarmAlert.this,
-                                   getString(R.string.alarm_alert_snooze_not_set,
-                                             Alarms.formatTime(AlarmAlert.this, c)),
-                                   Toast.LENGTH_LONG).show();
-                } else {
-                    Toast.makeText(AlarmAlert.this,
-                                   getString(R.string.alarm_alert_snooze_set,
-                                             SNOOZE_MINUTES),
-                                   Toast.LENGTH_LONG).show();
-
-                    Alarms.saveSnoozeAlert(AlarmAlert.this, mAlarmId, snoozeTarget);
-                    Alarms.setNextAlert(AlarmAlert.this);
-                    mSnoozed = true;
-                }
-                disableKiller();
-                cleanupAlarm();
-                releaseLocks();
-                finish();
-            }
-        });
-
-        /* dismiss button: close notification */
-        findViewById(R.id.dismiss).setOnClickListener(new Button.OnClickListener() {
+        // If this was a configuration change, keep the silenced text if the
+        // alarm was killed.
+        if (mState == KILLED) {
+            updateSilencedText();
+            mSnoozeButton.setEnabled(false);
+        } else {
+            mSnoozeButton.setOnClickListener(new Button.OnClickListener() {
                 public void onClick(View v) {
-                    disableKiller();
-                    cleanupAlarm();
-                    releaseLocks();
+                    snooze();
                     finish();
                 }
             });
+        }
+
+        /* dismiss button: close notification */
+        findViewById(R.id.dismiss).setOnClickListener(
+                new Button.OnClickListener() {
+                    public void onClick(View v) {
+                        dismiss();
+                        finish();
+                    }
+                });
+    }
+
+    // Attempt to snooze this alert.
+    private void snooze() {
+        if (mState != UNKNOWN) {
+            return;
+        }
+        // If the next alarm is set for sooner than the snooze interval, don't
+        // snooze. Instead, toast the user that the snooze will not be set.
+        final long snoozeTime = System.currentTimeMillis()
+                + (1000 * 60 * SNOOZE_MINUTES);
+        final long nextAlarm =
+                Alarms.calculateNextAlert(AlarmAlert.this).getAlert();
+        String displayTime = null;
+        if (nextAlarm < snoozeTime) {
+            final Calendar c = Calendar.getInstance();
+            c.setTimeInMillis(nextAlarm);
+            displayTime = getString(R.string.alarm_alert_snooze_not_set,
+                    Alarms.formatTime(AlarmAlert.this, c));
+            mState = DISMISS;
+        } else {
+            Alarms.saveSnoozeAlert(AlarmAlert.this, mAlarmId, snoozeTime,
+                    mLabel);
+            Alarms.setNextAlert(AlarmAlert.this);
+            displayTime = getString(R.string.alarm_alert_snooze_set,
+                    SNOOZE_MINUTES);
+            mState = SNOOZE;
+        }
+        // Intentionally log the snooze time for debugging.
+        Log.v(displayTime);
+        // Display the snooze minutes in a toast.
+        Toast.makeText(AlarmAlert.this, displayTime, Toast.LENGTH_LONG).show();
+        mKlaxon.stop(this, mState == SNOOZE);
+        releaseLocks();
+    }
+
+    // Dismiss the alarm.
+    private void dismiss() {
+        if (mState != UNKNOWN) {
+            return;
+        }
+        mState = DISMISS;
+        mKlaxon.stop(this, false);
+        releaseLocks();
     }
 
     /**
@@ -155,12 +233,20 @@ public class AlarmAlert extends Activity implements Alarms.AlarmSettings {
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
         if (Log.LOGV) Log.v("AlarmAlert.OnNewIntent()");
+        mState = UNKNOWN;
         mSnoozeButton.setEnabled(true);
         disableKeyguard();
-        cleanupAlarm();
-        mCleanupCalled = false;
-        disableKiller();
-        playAlert(intent);
+
+        mAlarmId = intent.getIntExtra(Alarms.ID, -1);
+        // Play the new alarm sound.
+        mKlaxon.postPlay(this, mAlarmId);
+
+        setTitleFromIntent(intent);
+
+        /* unset silenced message */
+        TextView silenced = (TextView)findViewById(R.id.silencedText);
+        silenced.setVisibility(View.GONE);
+
         Alarms.setNextAlert(this);
         setIntent(intent);
     }
@@ -176,61 +262,53 @@ public class AlarmAlert extends Activity implements Alarms.AlarmSettings {
     protected void onStop() {
         super.onStop();
         if (Log.LOGV) Log.v("AlarmAlert.onStop()");
-        disableKiller();
-        cleanupAlarm();
-        releaseLocks();
+        // As a last resort, try to snooze if this activity is stopped.
+        snooze();
     }
 
-    /**
-     * kicks off audio/vibe alert
-     */
-    private void playAlert(Intent intent) {
-        mAlarmId = intent.getIntExtra(Alarms.ID, 0);
-        if (Log.LOGV) Log.v("playAlert() " + mAlarmId);
-
-        /* load audio alert */
-        ContentResolver cr = getContentResolver();
-        /* this will call reportAlarm() callback */
-        Alarms.getAlarm(cr, this, mAlarmId);
-
-        /* play audio alert */
-        if (mAlert == null) {
-            Log.e("Unable to play alarm: no audio file available");
-        } else {
-            Notification audio = new Notification();
-            audio.sound = Uri.parse(mAlert);
-            audio.audioStreamType = AudioManager.STREAM_ALARM;
-            audio.flags |= Notification.FLAG_INSISTENT;
-            if (mVibrate) audio.vibrate = sVibratePattern;
-            mNotificationManager.notify(AUDIO_ALERT_NOTIFICATION_ID, audio);
-        }
-        enableKiller();
-    }
-
-    /**
-     * Kills alarm audio after ALARM_TIMEOUT_SECONDS, so the alarm
-     * won't run all day.
-     *
-     * This just cancels the audio, but leaves the notification
-     * popped, so the user will know that the alarm tripped.
-     */
-    private void enableKiller() {
-        mTimeout = new Handler();
-        mTimeout.postDelayed(new Runnable() {
-                public void run() {
-                    if (Log.LOGV) Log.v("*********** Alarm killer triggered *************");
-                    /* don't allow snooze */
-                    mSnoozeButton.setEnabled(false);
-                    cleanupAlarm();
-                    releaseLocks();
-                }}, 1000 * ALARM_TIMEOUT_SECONDS);
+    @Override
+    public void onConfigurationChanged(Configuration config) {
+        super.onConfigurationChanged(config);
+        updateLayout();
     }
 
-    private void disableKiller() {
-        if (mTimeout != null) {
-            mTimeout.removeCallbacksAndMessages(null);
-            mTimeout = null;
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        // Do this on key down to handle a few of the system keys. Only handle
+        // the snooze and dismiss this alert if the state is unknown.
+        boolean up = event.getAction() == KeyEvent.ACTION_UP;
+        boolean dismiss = false;
+        switch (event.getKeyCode()) {
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+            // Ignore ENDCALL because we do not receive the event if the screen
+            // is on. However, we do receive the key up for ENDCALL if the
+            // screen was off.
+            case KeyEvent.KEYCODE_ENDCALL:
+                break;
+            // Volume keys dismiss the alarm
+            case KeyEvent.KEYCODE_VOLUME_UP:
+            case KeyEvent.KEYCODE_VOLUME_DOWN:
+                dismiss = true;
+            // All other keys will snooze the alarm
+            default:
+                // Check for UNKNOWN here so that we intercept both key events
+                // and prevent the volume keys from triggering their default
+                // behavior.
+                if (mState == UNKNOWN && up) {
+                    if (dismiss) {
+                        dismiss();
+                    } else {
+                        snooze();
+                    }
+                    finish();
+                }
+                return true;
         }
+        return super.dispatchKeyEvent(event);
     }
 
     private synchronized void enableKeyguard() {
@@ -254,23 +332,4 @@ public class AlarmAlert extends Activity implements Alarms.AlarmSettings {
         AlarmAlertWakeLock.release();
         enableKeyguard();
     }
-
-    /**
-     * Stops alarm audio and disables alarm if it not snoozed and not
-     * repeating
-     */
-    private synchronized void cleanupAlarm() {
-        if (Log.LOGV) Log.v("cleanupAlarm " + mAlarmId);
-        if (!mCleanupCalled) {
-            mCleanupCalled = true;
-
-            // Stop audio playing
-            mNotificationManager.cancel(AUDIO_ALERT_NOTIFICATION_ID);
-
-            /* disable alarm only if it is not set to repeat */
-            if (!mSnoozed && ((mDaysOfWeek == null || !mDaysOfWeek.isRepeatSet()))) {
-                Alarms.enableAlarm(AlarmAlert.this, mAlarmId, false);
-            }
-        }
-    }
 }