OSDN Git Service

Add a setting to change the behavior of the side buttons during an alarm.
[android-x86/packages-apps-DeskClock.git] / src / com / android / alarmclock / AlarmAlert.java
index 6db9aad..038521b 100644 (file)
 package com.android.alarmclock;
 
 import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.BroadcastReceiver;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.preference.PreferenceManager;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -39,187 +46,159 @@ import java.util.Calendar;
  */
 public class AlarmAlert extends Activity {
 
-    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 Button mSnoozeButton;
-    private int mState = UNKNOWN;
+    // These defaults must match the values in res/xml/settings.xml
+    private static final String DEFAULT_SNOOZE = "10";
+    private static final String DEFAULT_VOLUME_BEHAVIOR = "2";
 
-    private AlarmKlaxon mKlaxon;
-    private int mAlarmId;
-    private String mLabel;
+    private Alarm mAlarm;
+    private int mVolumeBehavior;
+
+    // Receives the ALARM_KILLED action from the AlarmKlaxon.
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Alarm alarm = intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
+            if (mAlarm.id == alarm.id) {
+                dismiss(true);
+            }
+        }
+    };
 
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        // 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;
-        Window win = getWindow();
-        win.setAttributes(lp);
-        // TODO Make the activity full screen for FLAG_SHOW_WHEN_LOCKED to bypass
-        // the key guard.
-        win.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND  |
-                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
-
-        Intent i = getIntent();
-        mAlarmId = i.getIntExtra(Alarms.ID, -1);
-
-        mKlaxon = new AlarmKlaxon();
-        mKlaxon.postPlay(this, mAlarmId);
-
-        /* Set the title from the passed in label */
-        setTitleFromIntent(i);
-
-        /* allow next alarm to trigger while this activity is
-           active */
-        Alarms.disableSnoozeAlert(AlarmAlert.this);
-        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;
-            }
-        });
+        mAlarm = getIntent().getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
+
+        // Get the volume/camera button behavior setting
+        final String vol =
+                PreferenceManager.getDefaultSharedPreferences(this)
+                .getString(SettingsActivity.KEY_VOLUME_BEHAVIOR,
+                        DEFAULT_VOLUME_BEHAVIOR);
+        mVolumeBehavior = Integer.parseInt(vol);
 
+        requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
         updateLayout();
+
+        // Register to get the alarm killed intent.
+        registerReceiver(mReceiver, new IntentFilter(Alarms.ALARM_KILLED));
     }
 
-    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 setTitle() {
+        String label = mAlarm.getLabelOrDefault(this);
+        TextView title = (TextView) findViewById(R.id.alertTitle);
+        title.setText(label);
     }
 
-    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);
+    // This method is overwritten in AlarmAlertFullScreen in order to show a
+    // full activity with the wallpaper as the background.
+    protected View inflateView(LayoutInflater inflater) {
+        return inflater.inflate(R.layout.alarm_alert, null);
     }
 
     private void updateLayout() {
-        setContentView(R.layout.alarm_alert);
+        LayoutInflater inflater = LayoutInflater.from(this);
+
+        setContentView(inflateView(inflater));
 
         /* 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);
+        inflater.inflate(AlarmClock.CLOCKS[face], clockView);
+        View clockLayout = findViewById(R.id.clock);
         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();
-        // 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) {
-                    snooze();
-                    finish();
-                }
-            });
-        }
+        Button snooze = (Button) findViewById(R.id.snooze);
+        snooze.requestFocus();
+        snooze.setOnClickListener(new Button.OnClickListener() {
+            public void onClick(View v) {
+                snooze();
+            }
+        });
 
         /* dismiss button: close notification */
         findViewById(R.id.dismiss).setOnClickListener(
                 new Button.OnClickListener() {
                     public void onClick(View v) {
-                        dismiss();
-                        finish();
+                        dismiss(false);
                     }
                 });
+
+        /* Set the title from the passed in alarm */
+        setTitle();
     }
 
     // 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 String snooze =
+                PreferenceManager.getDefaultSharedPreferences(this)
+                .getString(SettingsActivity.KEY_ALARM_SNOOZE, DEFAULT_SNOOZE);
+        int snoozeMinutes = Integer.parseInt(snooze);
+
         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;
-        }
+                + (1000 * 60 * snoozeMinutes);
+        Alarms.saveSnoozeAlert(AlarmAlert.this, mAlarm.id, snoozeTime);
+
+        // Get the display time for the snooze and update the notification.
+        final Calendar c = Calendar.getInstance();
+        c.setTimeInMillis(snoozeTime);
+
+        // Append (snoozed) to the label.
+        String label = mAlarm.getLabelOrDefault(this);
+        label = getString(R.string.alarm_notify_snooze_label, label);
+
+        // Notify the user that the alarm has been snoozed.
+        Intent cancelSnooze = new Intent(this, AlarmReceiver.class);
+        cancelSnooze.setAction(Alarms.CANCEL_SNOOZE);
+        cancelSnooze.putExtra(Alarms.ALARM_ID, mAlarm.id);
+        PendingIntent broadcast =
+                PendingIntent.getBroadcast(this, mAlarm.id, cancelSnooze, 0);
+        NotificationManager nm = getNotificationManager();
+        Notification n = new Notification(R.drawable.stat_notify_alarm,
+                label, 0);
+        n.setLatestEventInfo(this, label,
+                getString(R.string.alarm_notify_snooze_text,
+                    Alarms.formatTime(this, c)), broadcast);
+        n.deleteIntent = broadcast;
+        n.flags |= Notification.FLAG_AUTO_CANCEL;
+        nm.notify(mAlarm.id, n);
+
+        String displayTime = getString(R.string.alarm_alert_snooze_set,
+                snoozeMinutes);
         // 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();
+        stopService(new Intent(Alarms.ALARM_ALERT_ACTION));
+        finish();
+    }
+
+    private NotificationManager getNotificationManager() {
+        return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
     }
 
     // Dismiss the alarm.
-    private void dismiss() {
-        if (mState != UNKNOWN) {
-            return;
+    private void dismiss(boolean killed) {
+        // The service told us that the alarm has been killed, do not modify
+        // the notification or stop the service.
+        if (!killed) {
+            // Cancel the notification and stop playing the alarm
+            NotificationManager nm = getNotificationManager();
+            nm.cancel(mAlarm.id);
+            stopService(new Intent(Alarms.ALARM_ALERT_ACTION));
         }
-        mState = DISMISS;
-        mKlaxon.stop(this, false);
-        releaseLocks();
+        finish();
     }
 
     /**
@@ -229,91 +208,66 @@ public class AlarmAlert extends Activity {
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
-        if (Log.LOGV) Log.v("AlarmAlert.OnNewIntent()");
-        mState = UNKNOWN;
-        mSnoozeButton.setEnabled(true);
-
-        mAlarmId = intent.getIntExtra(Alarms.ID, -1);
-        // Play the new alarm sound.
-        mKlaxon.postPlay(this, mAlarmId);
 
-        setTitleFromIntent(intent);
+        if (Log.LOGV) Log.v("AlarmAlert.OnNewIntent()");
 
-        /* unset silenced message */
-        TextView silenced = (TextView)findViewById(R.id.silencedText);
-        silenced.setVisibility(View.GONE);
+        mAlarm = intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
 
-        Alarms.setNextAlert(this);
-        setIntent(intent);
+        setTitle();
     }
 
     @Override
-    protected void onResume() {
+    protected void onStart() {
         super.onResume();
-        if (Log.LOGV) Log.v("AlarmAlert.onResume()");
-        AlarmAlertWakeLock.acquire(this);
+        if (Log.LOGV) Log.v("AlarmAlert.onStart()");
+        // Acquire a separate lock for the screen to stay on. This is necessary
+        // to avoid flashing the keyguard when the screen is locked.
+        AlarmAlertWakeLock.acquireScreenWakeLock(this);
     }
 
     @Override
     protected void onStop() {
-        super.onStop();
+        super.onPause();
         if (Log.LOGV) Log.v("AlarmAlert.onStop()");
-        // As a last resort, try to snooze if this activity is stopped.
-        snooze();
-        // We might have been killed by the KillerCallback so always release
-        // the lock and keyguard.
-        releaseLocks();
+        AlarmAlertWakeLock.releaseScreenLock();
     }
 
     @Override
-    public void onConfigurationChanged(Configuration config) {
-        super.onConfigurationChanged(config);
-        updateLayout();
+    public void onDestroy() {
+        super.onDestroy();
+        if (Log.LOGV) Log.v("AlarmAlert.onDestroy()");
+        // No longer care about the alarm being killed.
+        unregisterReceiver(mReceiver);
     }
 
     @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.
+        // Do this on key down to handle a few of the system keys.
         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
+            // Volume keys and camera 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();
+            case KeyEvent.KEYCODE_CAMERA:
+            case KeyEvent.KEYCODE_FOCUS:
+                if (up) {
+                    switch (mVolumeBehavior) {
+                        case 1:
+                            snooze();
+                            break;
+
+                        case 2:
+                            dismiss(false);
+                            break;
+
+                        default:
+                            break;
                     }
-                    finish();
                 }
                 return true;
+            default:
+                break;
         }
         return super.dispatchKeyEvent(event);
     }
-
-    /**
-     * release wake and keyguard locks
-     */
-    private synchronized void releaseLocks() {
-        AlarmAlertWakeLock.release();
-    }
 }