1/*
2 * Copyright (C) 2009 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
17package com.android.deskclock;
18
19import android.app.Activity;
20import android.app.Notification;
21import android.app.NotificationManager;
22import android.app.PendingIntent;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.os.Bundle;
28import android.preference.PreferenceManager;
29import android.view.KeyEvent;
30import android.view.LayoutInflater;
31import android.view.View;
32import android.view.Window;
33import android.view.WindowManager;
34import android.widget.Button;
35import android.widget.TextView;
36import android.widget.Toast;
37
38import java.util.Calendar;
39
40/**
41 * Alarm Clock alarm alert: pops visible indicator and plays alarm
42 * tone. This activity is the full screen version which shows over the lock
43 * screen with the wallpaper as the background.
44 */
45public class AlarmAlertFullScreen extends Activity {
46
47    // These defaults must match the values in res/xml/settings.xml
48    private static final String DEFAULT_SNOOZE = "10";
49    private static final String DEFAULT_VOLUME_BEHAVIOR = "2";
50    protected static final String SCREEN_OFF = "screen_off";
51
52    protected Alarm mAlarm;
53    private int mVolumeBehavior;
54    boolean mFullscreenStyle;
55
56    // Receives the ALARM_KILLED action from the AlarmKlaxon,
57    // and also ALARM_SNOOZE_ACTION / ALARM_DISMISS_ACTION from other applications
58    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
59        @Override
60        public void onReceive(Context context, Intent intent) {
61            String action = intent.getAction();
62            if (action.equals(Alarms.ALARM_SNOOZE_ACTION)) {
63                snooze();
64            } else if (action.equals(Alarms.ALARM_DISMISS_ACTION)) {
65                dismiss(false);
66            } else {
67                Alarm alarm = intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
68                if (alarm != null && mAlarm.id == alarm.id) {
69                    dismiss(true);
70                }
71            }
72        }
73    };
74
75    @Override
76    protected void onCreate(Bundle icicle) {
77        super.onCreate(icicle);
78
79        mAlarm = getIntent().getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
80
81        // Get the volume/camera button behavior setting
82        final String vol =
83                PreferenceManager.getDefaultSharedPreferences(this)
84                .getString(SettingsActivity.KEY_VOLUME_BEHAVIOR,
85                        DEFAULT_VOLUME_BEHAVIOR);
86        mVolumeBehavior = Integer.parseInt(vol);
87
88        final Window win = getWindow();
89        win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
90                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
91        // Turn on the screen unless we are being launched from the AlarmAlert
92        // subclass as a result of the screen turning off.
93        if (!getIntent().getBooleanExtra(SCREEN_OFF, false)) {
94            win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
95                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
96                    | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON);
97        }
98
99        updateLayout();
100
101        // Register to get the alarm killed/snooze/dismiss intent.
102        IntentFilter filter = new IntentFilter(Alarms.ALARM_KILLED);
103        filter.addAction(Alarms.ALARM_SNOOZE_ACTION);
104        filter.addAction(Alarms.ALARM_DISMISS_ACTION);
105        registerReceiver(mReceiver, filter);
106    }
107
108    private void setTitle() {
109        final String titleText = mAlarm.getLabelOrDefault(this);
110
111        setTitle(titleText);
112    }
113
114    protected int getLayoutResId() {
115        return R.layout.alarm_alert_fullscreen;
116    }
117
118    private void updateLayout() {
119        LayoutInflater inflater = LayoutInflater.from(this);
120
121        setContentView(inflater.inflate(getLayoutResId(), null));
122
123        /* snooze behavior: pop a snooze confirmation view, kick alarm
124           manager. */
125        Button snooze = (Button) findViewById(R.id.snooze);
126        snooze.requestFocus();
127        snooze.setOnClickListener(new Button.OnClickListener() {
128            public void onClick(View v) {
129                snooze();
130            }
131        });
132
133        /* dismiss button: close notification */
134        findViewById(R.id.dismiss).setOnClickListener(
135                new Button.OnClickListener() {
136                    public void onClick(View v) {
137                        dismiss(false);
138                    }
139                });
140
141        /* Set the title from the passed in alarm */
142        setTitle();
143    }
144
145    // Attempt to snooze this alert.
146    private void snooze() {
147        // Do not snooze if the snooze button is disabled.
148        if (!findViewById(R.id.snooze).isEnabled()) {
149            dismiss(false);
150            return;
151        }
152        final String snooze =
153                PreferenceManager.getDefaultSharedPreferences(this)
154                .getString(SettingsActivity.KEY_ALARM_SNOOZE, DEFAULT_SNOOZE);
155        int snoozeMinutes = Integer.parseInt(snooze);
156
157        final long snoozeTime = System.currentTimeMillis()
158                + (1000 * 60 * snoozeMinutes);
159        Alarms.saveSnoozeAlert(AlarmAlertFullScreen.this, mAlarm.id,
160                snoozeTime);
161
162        // Get the display time for the snooze and update the notification.
163        final Calendar c = Calendar.getInstance();
164        c.setTimeInMillis(snoozeTime);
165
166        // Append (snoozed) to the label.
167        String label = mAlarm.getLabelOrDefault(this);
168        label = getString(R.string.alarm_notify_snooze_label, label);
169
170        // Notify the user that the alarm has been snoozed.
171        Intent cancelSnooze = new Intent(this, AlarmReceiver.class);
172        cancelSnooze.setAction(Alarms.CANCEL_SNOOZE);
173        cancelSnooze.putExtra(Alarms.ALARM_INTENT_EXTRA, mAlarm);
174        PendingIntent broadcast =
175                PendingIntent.getBroadcast(this, mAlarm.id, cancelSnooze, 0);
176        NotificationManager nm = getNotificationManager();
177        Notification n = new Notification(R.drawable.stat_notify_alarm,
178                label, 0);
179        n.setLatestEventInfo(this, label,
180                getString(R.string.alarm_notify_snooze_text,
181                    Alarms.formatTime(this, c)), broadcast);
182        n.flags |= Notification.FLAG_AUTO_CANCEL
183                | Notification.FLAG_ONGOING_EVENT;
184        nm.notify(mAlarm.id, n);
185
186        String displayTime = getString(R.string.alarm_alert_snooze_set,
187                snoozeMinutes);
188        // Intentionally log the snooze time for debugging.
189        Log.v(displayTime);
190
191        // Display the snooze minutes in a toast.
192        Toast.makeText(AlarmAlertFullScreen.this, displayTime,
193                Toast.LENGTH_LONG).show();
194        stopService(new Intent(Alarms.ALARM_ALERT_ACTION));
195        finish();
196    }
197
198    private NotificationManager getNotificationManager() {
199        return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
200    }
201
202    // Dismiss the alarm.
203    private void dismiss(boolean killed) {
204        Log.i(killed ? "Alarm killed" : "Alarm dismissed by user");
205        // The service told us that the alarm has been killed, do not modify
206        // the notification or stop the service.
207        if (!killed) {
208            // Cancel the notification and stop playing the alarm
209            NotificationManager nm = getNotificationManager();
210            nm.cancel(mAlarm.id);
211            stopService(new Intent(Alarms.ALARM_ALERT_ACTION));
212        }
213        finish();
214    }
215
216    /**
217     * this is called when a second alarm is triggered while a
218     * previous alert window is still active.
219     */
220    @Override
221    protected void onNewIntent(Intent intent) {
222        super.onNewIntent(intent);
223
224        if (Log.LOGV) Log.v("AlarmAlert.OnNewIntent()");
225
226        mAlarm = intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
227
228        setTitle();
229    }
230
231    @Override
232    protected void onResume() {
233        super.onResume();
234        // If the alarm was deleted at some point, disable snooze.
235        if (Alarms.getAlarm(getContentResolver(), mAlarm.id) == null) {
236            Button snooze = (Button) findViewById(R.id.snooze);
237            snooze.setEnabled(false);
238        }
239    }
240
241    @Override
242    public void onDestroy() {
243        super.onDestroy();
244        if (Log.LOGV) Log.v("AlarmAlert.onDestroy()");
245        // No longer care about the alarm being killed.
246        unregisterReceiver(mReceiver);
247    }
248
249    @Override
250    public boolean dispatchKeyEvent(KeyEvent event) {
251        // Do this on key down to handle a few of the system keys.
252        boolean up = event.getAction() == KeyEvent.ACTION_UP;
253        switch (event.getKeyCode()) {
254            // Volume keys and camera keys dismiss the alarm
255            case KeyEvent.KEYCODE_VOLUME_UP:
256            case KeyEvent.KEYCODE_VOLUME_DOWN:
257            case KeyEvent.KEYCODE_VOLUME_MUTE:
258            case KeyEvent.KEYCODE_CAMERA:
259            case KeyEvent.KEYCODE_FOCUS:
260                if (up) {
261                    switch (mVolumeBehavior) {
262                        case 1:
263                            snooze();
264                            break;
265
266                        case 2:
267                            dismiss(false);
268                            break;
269
270                        default:
271                            break;
272                    }
273                }
274                return true;
275            default:
276                break;
277        }
278        return super.dispatchKeyEvent(event);
279    }
280
281    @Override
282    public void onBackPressed() {
283        // Don't allow back to dismiss. This method is overriden by AlarmAlert
284        // so that the dialog is dismissed.
285        return;
286    }
287}
288