OSDN Git Service

6db9aad64c0f17fe53803ae78ceea1940823b170
[android-x86/packages-apps-AlarmClock.git] / src / com / android / alarmclock / AlarmAlert.java
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.android.alarmclock;
18
19 import android.app.Activity;
20 import android.content.Intent;
21 import android.content.SharedPreferences;
22 import android.content.res.Configuration;
23 import android.os.Bundle;
24 import android.view.KeyEvent;
25 import android.view.View;
26 import android.view.ViewGroup;
27 import android.view.LayoutInflater;
28 import android.view.Window;
29 import android.view.WindowManager;
30 import android.widget.Button;
31 import android.widget.Toast;
32 import android.widget.TextView;
33
34 import java.util.Calendar;
35
36 /**
37  * Alarm Clock alarm alert: pops visible indicator and plays alarm
38  * tone
39  */
40 public class AlarmAlert extends Activity {
41
42     private static final int SNOOZE_MINUTES = 10;
43     private static final int UNKNOWN = 0;
44     private static final int SNOOZE = 1;
45     private static final int DISMISS = 2;
46     private static final int KILLED = 3;
47     private Button mSnoozeButton;
48     private int mState = UNKNOWN;
49
50     private AlarmKlaxon mKlaxon;
51     private int mAlarmId;
52     private String mLabel;
53
54     @Override
55     protected void onCreate(Bundle icicle) {
56         super.onCreate(icicle);
57
58         // Maintain a lock during the playback of the alarm. This lock may have
59         // already been acquired in AlarmReceiver. If the process was killed,
60         // the global wake lock is gone. Acquire again just to be sure.
61        // AlarmAlertWakeLock.acquire(this);
62
63         /* FIXME Intentionally verbose: always log this until we've
64            fully debugged the app failing to start up */
65         Log.v("AlarmAlert.onCreate()");
66
67         // Popup alert over black screen
68         WindowManager.LayoutParams lp = getWindow().getAttributes();
69         lp.width = ViewGroup.LayoutParams.WRAP_CONTENT;
70         lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
71         // XXX DO NOT COPY THIS!!!  THIS IS BOGUS!  Making an activity have
72         // a system alert type is completely broken, because the activity
73         // manager will still hide/show it as if it is part of the normal
74         // activity stack.  If this is really what you want and you want it
75         // to work correctly, you should create and show your own custom window.
76         lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
77         lp.token = null;
78         Window win = getWindow();
79         win.setAttributes(lp);
80         // TODO Make the activity full screen for FLAG_SHOW_WHEN_LOCKED to bypass
81         // the key guard.
82         win.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND  |
83                 WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
84
85         Intent i = getIntent();
86         mAlarmId = i.getIntExtra(Alarms.ID, -1);
87
88         mKlaxon = new AlarmKlaxon();
89         mKlaxon.postPlay(this, mAlarmId);
90
91         /* Set the title from the passed in label */
92         setTitleFromIntent(i);
93
94         /* allow next alarm to trigger while this activity is
95            active */
96         Alarms.disableSnoozeAlert(AlarmAlert.this);
97         Alarms.disableAlert(AlarmAlert.this, mAlarmId);
98         Alarms.setNextAlert(this);
99
100         mKlaxon.setKillerCallback(new AlarmKlaxon.KillerCallback() {
101             public void onKilled() {
102                 if (Log.LOGV) Log.v("onKilled()");
103                 updateSilencedText();
104
105                 /* don't allow snooze */
106                 mSnoozeButton.setEnabled(false);
107
108                 // Dismiss the alarm but mark the state as killed so if the
109                 // config changes, we show the silenced message and disable
110                 // snooze.
111                 dismiss();
112                 mState = KILLED;
113             }
114         });
115
116         updateLayout();
117     }
118
119     private void setTitleFromIntent(Intent i) {
120         mLabel = i.getStringExtra(Alarms.LABEL);
121         if (mLabel == null || mLabel.length() == 0) {
122             mLabel = getString(R.string.default_label);
123         }
124         setTitle(mLabel);
125     }
126
127     private void updateSilencedText() {
128         TextView silenced = (TextView) findViewById(R.id.silencedText);
129         silenced.setText(getString(R.string.alarm_alert_alert_silenced,
130                     AlarmKlaxon.ALARM_TIMEOUT_SECONDS / 60));
131         silenced.setVisibility(View.VISIBLE);
132     }
133
134     private void updateLayout() {
135         setContentView(R.layout.alarm_alert);
136
137         /* set clock face */
138         LayoutInflater mFactory = LayoutInflater.from(this);
139         SharedPreferences settings =
140                 getSharedPreferences(AlarmClock.PREFERENCES, 0);
141         int face = settings.getInt(AlarmClock.PREF_CLOCK_FACE, 0);
142         if (face < 0 || face >= AlarmClock.CLOCKS.length) {
143             face = 0;
144         }
145         View clockLayout =
146                 (View) mFactory.inflate(AlarmClock.CLOCKS[face], null);
147         ViewGroup clockView = (ViewGroup) findViewById(R.id.clockView);
148         clockView.addView(clockLayout);
149         if (clockLayout instanceof DigitalClock) {
150             ((DigitalClock) clockLayout).setAnimate();
151         }
152
153         /* snooze behavior: pop a snooze confirmation view, kick alarm
154            manager. */
155         mSnoozeButton = (Button) findViewById(R.id.snooze);
156         mSnoozeButton.requestFocus();
157         // If this was a configuration change, keep the silenced text if the
158         // alarm was killed.
159         if (mState == KILLED) {
160             updateSilencedText();
161             mSnoozeButton.setEnabled(false);
162         } else {
163             mSnoozeButton.setOnClickListener(new Button.OnClickListener() {
164                 public void onClick(View v) {
165                     snooze();
166                     finish();
167                 }
168             });
169         }
170
171         /* dismiss button: close notification */
172         findViewById(R.id.dismiss).setOnClickListener(
173                 new Button.OnClickListener() {
174                     public void onClick(View v) {
175                         dismiss();
176                         finish();
177                     }
178                 });
179     }
180
181     // Attempt to snooze this alert.
182     private void snooze() {
183         if (mState != UNKNOWN) {
184             return;
185         }
186         // If the next alarm is set for sooner than the snooze interval, don't
187         // snooze. Instead, toast the user that the snooze will not be set.
188         final long snoozeTime = System.currentTimeMillis()
189                 + (1000 * 60 * SNOOZE_MINUTES);
190         final long nextAlarm =
191                 Alarms.calculateNextAlert(AlarmAlert.this).getAlert();
192         String displayTime = null;
193         if (nextAlarm < snoozeTime) {
194             final Calendar c = Calendar.getInstance();
195             c.setTimeInMillis(nextAlarm);
196             displayTime = getString(R.string.alarm_alert_snooze_not_set,
197                     Alarms.formatTime(AlarmAlert.this, c));
198             mState = DISMISS;
199         } else {
200             Alarms.saveSnoozeAlert(AlarmAlert.this, mAlarmId, snoozeTime,
201                     mLabel);
202             Alarms.setNextAlert(AlarmAlert.this);
203             displayTime = getString(R.string.alarm_alert_snooze_set,
204                     SNOOZE_MINUTES);
205             mState = SNOOZE;
206         }
207         // Intentionally log the snooze time for debugging.
208         Log.v(displayTime);
209         // Display the snooze minutes in a toast.
210         Toast.makeText(AlarmAlert.this, displayTime, Toast.LENGTH_LONG).show();
211         mKlaxon.stop(this, mState == SNOOZE);
212         releaseLocks();
213     }
214
215     // Dismiss the alarm.
216     private void dismiss() {
217         if (mState != UNKNOWN) {
218             return;
219         }
220         mState = DISMISS;
221         mKlaxon.stop(this, false);
222         releaseLocks();
223     }
224
225     /**
226      * this is called when a second alarm is triggered while a
227      * previous alert window is still active.
228      */
229     @Override
230     protected void onNewIntent(Intent intent) {
231         super.onNewIntent(intent);
232         if (Log.LOGV) Log.v("AlarmAlert.OnNewIntent()");
233         mState = UNKNOWN;
234         mSnoozeButton.setEnabled(true);
235
236         mAlarmId = intent.getIntExtra(Alarms.ID, -1);
237         // Play the new alarm sound.
238         mKlaxon.postPlay(this, mAlarmId);
239
240         setTitleFromIntent(intent);
241
242         /* unset silenced message */
243         TextView silenced = (TextView)findViewById(R.id.silencedText);
244         silenced.setVisibility(View.GONE);
245
246         Alarms.setNextAlert(this);
247         setIntent(intent);
248     }
249
250     @Override
251     protected void onResume() {
252         super.onResume();
253         if (Log.LOGV) Log.v("AlarmAlert.onResume()");
254         AlarmAlertWakeLock.acquire(this);
255     }
256
257     @Override
258     protected void onStop() {
259         super.onStop();
260         if (Log.LOGV) Log.v("AlarmAlert.onStop()");
261         // As a last resort, try to snooze if this activity is stopped.
262         snooze();
263         // We might have been killed by the KillerCallback so always release
264         // the lock and keyguard.
265         releaseLocks();
266     }
267
268     @Override
269     public void onConfigurationChanged(Configuration config) {
270         super.onConfigurationChanged(config);
271         updateLayout();
272     }
273
274     @Override
275     public boolean dispatchKeyEvent(KeyEvent event) {
276         // Do this on key down to handle a few of the system keys. Only handle
277         // the snooze and dismiss this alert if the state is unknown.
278         boolean up = event.getAction() == KeyEvent.ACTION_UP;
279         boolean dismiss = false;
280         switch (event.getKeyCode()) {
281             case KeyEvent.KEYCODE_DPAD_UP:
282             case KeyEvent.KEYCODE_DPAD_DOWN:
283             case KeyEvent.KEYCODE_DPAD_LEFT:
284             case KeyEvent.KEYCODE_DPAD_RIGHT:
285             case KeyEvent.KEYCODE_DPAD_CENTER:
286             // Ignore ENDCALL because we do not receive the event if the screen
287             // is on. However, we do receive the key up for ENDCALL if the
288             // screen was off.
289             case KeyEvent.KEYCODE_ENDCALL:
290                 break;
291             // Volume keys dismiss the alarm
292             case KeyEvent.KEYCODE_VOLUME_UP:
293             case KeyEvent.KEYCODE_VOLUME_DOWN:
294                 dismiss = true;
295             // All other keys will snooze the alarm
296             default:
297                 // Check for UNKNOWN here so that we intercept both key events
298                 // and prevent the volume keys from triggering their default
299                 // behavior.
300                 if (mState == UNKNOWN && up) {
301                     if (dismiss) {
302                         dismiss();
303                     } else {
304                         snooze();
305                     }
306                     finish();
307                 }
308                 return true;
309         }
310         return super.dispatchKeyEvent(event);
311     }
312
313     /**
314      * release wake and keyguard locks
315      */
316     private synchronized void releaseLocks() {
317         AlarmAlertWakeLock.release();
318     }
319 }