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.alarmclock;
19 import android.app.Activity;
20 import android.app.KeyguardManager;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.SharedPreferences;
24 import android.content.res.Configuration;
25 import android.graphics.PixelFormat;
26 import android.os.Bundle;
27 import android.view.KeyEvent;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.view.LayoutInflater;
31 import android.view.WindowManager;
32 import android.widget.Button;
33 import android.widget.Toast;
34 import android.widget.TextView;
36 import java.util.Calendar;
39 * Alarm Clock alarm alert: pops visible indicator and plays alarm
42 public class AlarmAlert extends Activity {
44 private static final int SNOOZE_MINUTES = 10;
45 private static final int UNKNOWN = 0;
46 private static final int SNOOZE = 1;
47 private static final int DISMISS = 2;
48 private static final int KILLED = 3;
50 private KeyguardManager mKeyguardManager;
51 private KeyguardManager.KeyguardLock mKeyguardLock;
52 private Button mSnoozeButton;
53 private int mState = UNKNOWN;
55 private AlarmKlaxon mKlaxon;
57 private String mLabel;
60 protected void onCreate(Bundle icicle) {
61 super.onCreate(icicle);
63 // Maintain a lock during the playback of the alarm. This lock may have
64 // already been acquired in AlarmReceiver. If the process was killed,
65 // the global wake lock is gone. Acquire again just to be sure.
66 AlarmAlertWakeLock.acquire(this);
68 /* FIXME Intentionally verbose: always log this until we've
69 fully debugged the app failing to start up */
70 Log.v("AlarmAlert.onCreate()");
72 // Popup alert over black screen
73 WindowManager.LayoutParams lp = getWindow().getAttributes();
74 lp.width = ViewGroup.LayoutParams.WRAP_CONTENT;
75 lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
76 // XXX DO NOT COPY THIS!!! THIS IS BOGUS! Making an activity have
77 // a system alert type is completely broken, because the activity
78 // manager will still hide/show it as if it is part of the normal
79 // activity stack. If this is really what you want and you want it
80 // to work correctly, you should create and show your own custom window.
81 lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
83 getWindow().setAttributes(lp);
84 getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
86 mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
88 Intent i = getIntent();
89 mAlarmId = i.getIntExtra(Alarms.ID, -1);
91 mKlaxon = new AlarmKlaxon();
92 mKlaxon.postPlay(this, mAlarmId);
94 /* Set the title from the passed in label */
95 setTitleFromIntent(i);
97 /* allow next alarm to trigger while this activity is
99 Alarms.disableSnoozeAlert(AlarmAlert.this);
100 Alarms.disableAlert(AlarmAlert.this, mAlarmId);
101 Alarms.setNextAlert(this);
103 mKlaxon.setKillerCallback(new AlarmKlaxon.KillerCallback() {
104 public void onKilled() {
105 if (Log.LOGV) Log.v("onKilled()");
106 updateSilencedText();
108 /* don't allow snooze */
109 mSnoozeButton.setEnabled(false);
111 // Dismiss the alarm but mark the state as killed so if the
112 // config changes, we show the silenced message and disable
122 private void setTitleFromIntent(Intent i) {
123 mLabel = i.getStringExtra(Alarms.LABEL);
124 if (mLabel == null || mLabel.length() == 0) {
125 mLabel = getString(R.string.default_label);
130 private void updateSilencedText() {
131 TextView silenced = (TextView) findViewById(R.id.silencedText);
132 silenced.setText(getString(R.string.alarm_alert_alert_silenced,
133 AlarmKlaxon.ALARM_TIMEOUT_SECONDS / 60));
134 silenced.setVisibility(View.VISIBLE);
137 private void updateLayout() {
138 setContentView(R.layout.alarm_alert);
141 LayoutInflater mFactory = LayoutInflater.from(this);
142 SharedPreferences settings =
143 getSharedPreferences(AlarmClock.PREFERENCES, 0);
144 int face = settings.getInt(AlarmClock.PREF_CLOCK_FACE, 0);
145 if (face < 0 || face >= AlarmClock.CLOCKS.length) {
149 (View) mFactory.inflate(AlarmClock.CLOCKS[face], null);
150 ViewGroup clockView = (ViewGroup) findViewById(R.id.clockView);
151 clockView.addView(clockLayout);
152 if (clockLayout instanceof DigitalClock) {
153 ((DigitalClock) clockLayout).setAnimate();
156 /* snooze behavior: pop a snooze confirmation view, kick alarm
158 mSnoozeButton = (Button) findViewById(R.id.snooze);
159 mSnoozeButton.requestFocus();
160 // If this was a configuration change, keep the silenced text if the
162 if (mState == KILLED) {
163 updateSilencedText();
164 mSnoozeButton.setEnabled(false);
166 mSnoozeButton.setOnClickListener(new Button.OnClickListener() {
167 public void onClick(View v) {
174 /* dismiss button: close notification */
175 findViewById(R.id.dismiss).setOnClickListener(
176 new Button.OnClickListener() {
177 public void onClick(View v) {
184 // Attempt to snooze this alert.
185 private void snooze() {
186 if (mState != UNKNOWN) {
189 // If the next alarm is set for sooner than the snooze interval, don't
190 // snooze. Instead, toast the user that the snooze will not be set.
191 final long snoozeTime = System.currentTimeMillis()
192 + (1000 * 60 * SNOOZE_MINUTES);
193 final long nextAlarm =
194 Alarms.calculateNextAlert(AlarmAlert.this).getAlert();
195 String displayTime = null;
196 if (nextAlarm < snoozeTime) {
197 final Calendar c = Calendar.getInstance();
198 c.setTimeInMillis(nextAlarm);
199 displayTime = getString(R.string.alarm_alert_snooze_not_set,
200 Alarms.formatTime(AlarmAlert.this, c));
203 Alarms.saveSnoozeAlert(AlarmAlert.this, mAlarmId, snoozeTime,
205 Alarms.setNextAlert(AlarmAlert.this);
206 displayTime = getString(R.string.alarm_alert_snooze_set,
210 // Intentionally log the snooze time for debugging.
212 // Display the snooze minutes in a toast.
213 Toast.makeText(AlarmAlert.this, displayTime, Toast.LENGTH_LONG).show();
214 mKlaxon.stop(this, mState == SNOOZE);
218 // Dismiss the alarm.
219 private void dismiss() {
220 if (mState != UNKNOWN) {
224 mKlaxon.stop(this, false);
229 * this is called when a second alarm is triggered while a
230 * previous alert window is still active.
233 protected void onNewIntent(Intent intent) {
234 super.onNewIntent(intent);
235 if (Log.LOGV) Log.v("AlarmAlert.OnNewIntent()");
237 mSnoozeButton.setEnabled(true);
240 mAlarmId = intent.getIntExtra(Alarms.ID, -1);
241 // Play the new alarm sound.
242 mKlaxon.postPlay(this, mAlarmId);
244 setTitleFromIntent(intent);
246 /* unset silenced message */
247 TextView silenced = (TextView)findViewById(R.id.silencedText);
248 silenced.setVisibility(View.GONE);
250 Alarms.setNextAlert(this);
255 protected void onResume() {
257 if (Log.LOGV) Log.v("AlarmAlert.onResume()");
262 protected void onStop() {
264 if (Log.LOGV) Log.v("AlarmAlert.onStop()");
265 // As a last resort, try to snooze if this activity is stopped.
267 // We might have been killed by the KillerCallback so always release
268 // the lock and keyguard.
273 public void onConfigurationChanged(Configuration config) {
274 super.onConfigurationChanged(config);
279 public boolean dispatchKeyEvent(KeyEvent event) {
280 // Do this on key down to handle a few of the system keys. Only handle
281 // the snooze and dismiss this alert if the state is unknown.
282 boolean up = event.getAction() == KeyEvent.ACTION_UP;
283 boolean dismiss = false;
284 switch (event.getKeyCode()) {
285 case KeyEvent.KEYCODE_DPAD_UP:
286 case KeyEvent.KEYCODE_DPAD_DOWN:
287 case KeyEvent.KEYCODE_DPAD_LEFT:
288 case KeyEvent.KEYCODE_DPAD_RIGHT:
289 case KeyEvent.KEYCODE_DPAD_CENTER:
290 // Ignore ENDCALL because we do not receive the event if the screen
291 // is on. However, we do receive the key up for ENDCALL if the
293 case KeyEvent.KEYCODE_ENDCALL:
295 // Volume keys dismiss the alarm
296 case KeyEvent.KEYCODE_VOLUME_UP:
297 case KeyEvent.KEYCODE_VOLUME_DOWN:
299 // All other keys will snooze the alarm
301 // Check for UNKNOWN here so that we intercept both key events
302 // and prevent the volume keys from triggering their default
304 if (mState == UNKNOWN && up) {
314 return super.dispatchKeyEvent(event);
317 private synchronized void enableKeyguard() {
318 if (mKeyguardLock != null) {
319 mKeyguardLock.reenableKeyguard();
320 mKeyguardLock = null;
324 private synchronized void disableKeyguard() {
325 if (mKeyguardLock == null) {
326 mKeyguardLock = mKeyguardManager.newKeyguardLock(Log.LOGTAG);
327 mKeyguardLock.disableKeyguard();
332 * release wake and keyguard locks
334 private synchronized void releaseLocks() {
335 AlarmAlertWakeLock.release();