• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.deskclock;
18 
19 import android.app.Activity;
20 import android.app.Notification;
21 import android.app.NotificationManager;
22 import android.app.PendingIntent;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.pm.ActivityInfo;
28 import android.content.res.Configuration;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.Message;
32 import android.preference.PreferenceManager;
33 import android.view.KeyEvent;
34 import android.view.LayoutInflater;
35 import android.view.View;
36 import android.view.Window;
37 import android.view.WindowManager;
38 import android.widget.TextView;
39 import android.widget.Toast;
40 
41 import com.android.deskclock.widget.multiwaveview.GlowPadView;
42 
43 import java.util.Calendar;
44 
45 /**
46  * Alarm Clock alarm alert: pops visible indicator and plays alarm
47  * tone. This activity is the full screen version which shows over the lock
48  * screen with the wallpaper as the background.
49  */
50 public class AlarmAlertFullScreen extends Activity implements GlowPadView.OnTriggerListener {
51 
52     private final boolean LOG = true;
53     // These defaults must match the values in res/xml/settings.xml
54     private static final String DEFAULT_SNOOZE = "10";
55     protected static final String SCREEN_OFF = "screen_off";
56 
57     protected Alarm mAlarm;
58     private int mVolumeBehavior;
59     boolean mFullscreenStyle;
60     private GlowPadView mGlowPadView;
61     private boolean mIsDocked = false;
62 
63     // Parameters for the GlowPadView "ping" animation; see triggerPing().
64     private static final int PING_MESSAGE_WHAT = 101;
65     private static final boolean ENABLE_PING_AUTO_REPEAT = true;
66     private static final long PING_AUTO_REPEAT_DELAY_MSEC = 1200;
67 
68     private boolean mPingEnabled = true;
69 
70     // Receives the ALARM_KILLED action from the AlarmKlaxon,
71     // and also ALARM_SNOOZE_ACTION / ALARM_DISMISS_ACTION from other applications
72     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
73         @Override
74         public void onReceive(Context context, Intent intent) {
75             String action = intent.getAction();
76             if (LOG) {
77                 Log.v("AlarmAlertFullScreen - onReceive " + action);
78             }
79             if (action.equals(Alarms.ALARM_SNOOZE_ACTION)) {
80                 snooze();
81             } else if (action.equals(Alarms.ALARM_DISMISS_ACTION)) {
82                 dismiss(false, false);
83             } else {
84                 Alarm alarm = intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
85                 boolean replaced = intent.getBooleanExtra(Alarms.ALARM_REPLACED, false);
86                 if (alarm != null && mAlarm.id == alarm.id) {
87                     dismiss(true, replaced);
88                 }
89             }
90         }
91     };
92 
93     private final Handler mPingHandler = new Handler() {
94         @Override
95         public void handleMessage(Message msg) {
96             switch (msg.what) {
97                 case PING_MESSAGE_WHAT:
98                     triggerPing();
99                     break;
100             }
101         }
102     };
103 
104     @Override
onCreate(Bundle icicle)105     protected void onCreate(Bundle icicle) {
106         super.onCreate(icicle);
107 
108         mAlarm = getIntent().getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
109 
110         if (LOG) {
111             Log.v("AlarmAlertFullScreen - onCreate");
112             if (mAlarm != null) {
113                 Log.v("AlarmAlertFullScreen - Alarm Id " + mAlarm.toString());
114             }
115         }
116 
117         // Get the volume/camera button behavior setting
118         final String vol =
119                 PreferenceManager.getDefaultSharedPreferences(this)
120                 .getString(SettingsActivity.KEY_VOLUME_BEHAVIOR,
121                         SettingsActivity.DEFAULT_VOLUME_BEHAVIOR);
122         mVolumeBehavior = Integer.parseInt(vol);
123 
124         final Window win = getWindow();
125         win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
126                 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
127         // Turn on the screen unless we are being launched from the AlarmAlert
128         // subclass as a result of the screen turning off.
129         if (!getIntent().getBooleanExtra(SCREEN_OFF, false)) {
130             win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
131                     | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
132                     | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON);
133         }
134 
135         updateLayout();
136 
137         // Check the docking status , if the device is docked , do not limit rotation
138         IntentFilter ifilter = new IntentFilter(Intent.ACTION_DOCK_EVENT);
139         Intent dockStatus = registerReceiver(null, ifilter);
140         if (dockStatus != null) {
141             mIsDocked = dockStatus.getIntExtra(Intent.EXTRA_DOCK_STATE, -1)
142                     != Intent.EXTRA_DOCK_STATE_UNDOCKED;
143         }
144 
145         // Register to get the alarm killed/snooze/dismiss intent.
146         IntentFilter filter = new IntentFilter(Alarms.ALARM_KILLED);
147         filter.addAction(Alarms.ALARM_SNOOZE_ACTION);
148         filter.addAction(Alarms.ALARM_DISMISS_ACTION);
149         registerReceiver(mReceiver, filter);
150     }
151 
setTitle()152     private void setTitle() {
153         final String titleText = mAlarm.getLabelOrDefault(this);
154 
155         TextView tv = (TextView) findViewById(R.id.alertTitle);
156         tv.setText(titleText);
157 
158         setTitle(titleText);
159     }
160 
getLayoutResId()161     protected int getLayoutResId() {
162         return R.layout.alarm_alert;
163     }
164 
updateLayout()165     private void updateLayout() {
166         if (LOG) {
167             Log.v("AlarmAlertFullScreen - updateLayout");
168         }
169 
170         final LayoutInflater inflater = LayoutInflater.from(this);
171         final View view = inflater.inflate(getLayoutResId(), null);
172         view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
173         setContentView(view);
174 
175         /* Set the title from the passed in alarm */
176         setTitle();
177 
178         mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
179         mGlowPadView.setOnTriggerListener(this);
180         triggerPing();
181     }
182 
triggerPing()183     private void triggerPing() {
184         if (mPingEnabled) {
185             mGlowPadView.ping();
186 
187             if (ENABLE_PING_AUTO_REPEAT) {
188                 mPingHandler.sendEmptyMessageDelayed(PING_MESSAGE_WHAT, PING_AUTO_REPEAT_DELAY_MSEC);
189             }
190         }
191     }
192 
193     // Attempt to snooze this alert.
snooze()194     private void snooze() {
195         if (LOG) {
196             Log.v("AlarmAlertFullScreen - snooze");
197         }
198 
199         final String snooze =
200                 PreferenceManager.getDefaultSharedPreferences(this)
201                 .getString(SettingsActivity.KEY_ALARM_SNOOZE, DEFAULT_SNOOZE);
202         int snoozeMinutes = Integer.parseInt(snooze);
203 
204         final long snoozeTime = System.currentTimeMillis()
205                 + (1000 * 60 * snoozeMinutes);
206         Alarms.saveSnoozeAlert(AlarmAlertFullScreen.this, mAlarm.id,
207                 snoozeTime);
208 
209         // Get the display time for the snooze and update the notification.
210         final Calendar c = Calendar.getInstance();
211         c.setTimeInMillis(snoozeTime);
212         String snoozeTimeStr = Alarms.formatTime(this, c);
213         String label = mAlarm.getLabelOrDefault(this);
214 
215         // Notify the user that the alarm has been snoozed.
216         Intent dismissIntent = new Intent(this, AlarmReceiver.class);
217         dismissIntent.setAction(Alarms.CANCEL_SNOOZE);
218         dismissIntent.putExtra(Alarms.ALARM_INTENT_EXTRA, mAlarm);
219 
220         Intent openAlarm = new Intent(this, DeskClock.class);
221         openAlarm.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
222         openAlarm.putExtra(Alarms.ALARM_INTENT_EXTRA, mAlarm);
223         openAlarm.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.CLOCK_TAB_INDEX);
224 
225         NotificationManager nm = getNotificationManager();
226         Notification notif = new Notification.Builder(getApplicationContext())
227         .setContentTitle(label)
228         .setContentText(getResources().getString(R.string.alarm_alert_snooze_until, snoozeTimeStr))
229         .setSmallIcon(R.drawable.stat_notify_alarm)
230         .setOngoing(true)
231         .setAutoCancel(false)
232         .setPriority(Notification.PRIORITY_MAX)
233         .setWhen(0)
234         .addAction(android.R.drawable.ic_menu_close_clear_cancel,
235                 getResources().getString(R.string.alarm_alert_dismiss_text),
236                 PendingIntent.getBroadcast(this, mAlarm.id, dismissIntent, 0))
237         .build();
238         notif.contentIntent = PendingIntent.getActivity(this, mAlarm.id, openAlarm, 0);
239         nm.notify(mAlarm.id, notif);
240 
241         String displayTime = getString(R.string.alarm_alert_snooze_set,
242                 snoozeMinutes);
243         // Intentionally log the snooze time for debugging.
244         Log.v(displayTime);
245 
246         // Display the snooze minutes in a toast.
247         Toast.makeText(AlarmAlertFullScreen.this, displayTime,
248                 Toast.LENGTH_LONG).show();
249         stopService(new Intent(Alarms.ALARM_ALERT_ACTION));
250         finish();
251     }
252 
getNotificationManager()253     private NotificationManager getNotificationManager() {
254         return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
255     }
256 
257     // Dismiss the alarm.
dismiss(boolean killed, boolean replaced)258     private void dismiss(boolean killed, boolean replaced) {
259         if (LOG) {
260             Log.v("AlarmAlertFullScreen - dismiss");
261         }
262 
263         Log.i("Alarm id=" + mAlarm.id + (killed ? (replaced ? " replaced" : " killed") : " dismissed by user"));
264         // The service told us that the alarm has been killed, do not modify
265         // the notification or stop the service.
266         if (!killed) {
267             // Cancel the notification and stop playing the alarm
268             NotificationManager nm = getNotificationManager();
269             nm.cancel(mAlarm.id);
270             stopService(new Intent(Alarms.ALARM_ALERT_ACTION));
271         }
272         if (!replaced) {
273             finish();
274         }
275     }
276 
277     /**
278      * this is called when a second alarm is triggered while a
279      * previous alert window is still active.
280      */
281     @Override
onNewIntent(Intent intent)282     protected void onNewIntent(Intent intent) {
283         super.onNewIntent(intent);
284 
285         if (LOG) Log.v("AlarmAlert.OnNewIntent()");
286 
287         mAlarm = intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
288 
289         setTitle();
290     }
291 
292     @Override
onConfigurationChanged(Configuration newConfig)293     public void onConfigurationChanged(Configuration newConfig) {
294         if (LOG) {
295             Log.v("AlarmAlertFullScreen - onConfigChanged");
296         }
297         updateLayout();
298         super.onConfigurationChanged(newConfig);
299     }
300 
301     @Override
onResume()302     protected void onResume() {
303         super.onResume();
304         if (LOG) {
305             Log.v("AlarmAlertFullScreen - onResume");
306         }
307         // If the alarm was deleted at some point, disable snooze.
308         if (Alarms.getAlarm(getContentResolver(), mAlarm.id) == null) {
309             mGlowPadView.setTargetResources(R.array.dismiss_drawables);
310             mGlowPadView.setTargetDescriptionsResourceId(R.array.dismiss_descriptions);
311             mGlowPadView.setDirectionDescriptionsResourceId(R.array.dismiss_direction_descriptions);
312         }
313         // The activity is locked to the default orientation as a default set in the manifest
314         // Override this settings if the device is docked or config set it differently
315         if (getResources().getBoolean(R.bool.config_rotateAlarmAlert) || mIsDocked) {
316             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
317         }
318     }
319 
320     @Override
onDestroy()321     public void onDestroy() {
322         super.onDestroy();
323         if (LOG) Log.v("AlarmAlertFullScreen.onDestroy()");
324         // No longer care about the alarm being killed.
325         unregisterReceiver(mReceiver);
326     }
327 
328     @Override
dispatchKeyEvent(KeyEvent event)329     public boolean dispatchKeyEvent(KeyEvent event) {
330         // Do this on key down to handle a few of the system keys.
331         boolean up = event.getAction() == KeyEvent.ACTION_UP;
332         if (LOG) {
333             Log.v("AlarmAlertFullScreen - dispatchKeyEvent " + event.getKeyCode());
334         }
335         switch (event.getKeyCode()) {
336             // Volume keys and camera keys dismiss the alarm
337             case KeyEvent.KEYCODE_POWER:
338             case KeyEvent.KEYCODE_VOLUME_UP:
339             case KeyEvent.KEYCODE_VOLUME_DOWN:
340             case KeyEvent.KEYCODE_VOLUME_MUTE:
341             case KeyEvent.KEYCODE_CAMERA:
342             case KeyEvent.KEYCODE_FOCUS:
343                 if (up) {
344                     switch (mVolumeBehavior) {
345                         case 1:
346                             snooze();
347                             break;
348 
349                         case 2:
350                             dismiss(false, false);
351                             break;
352 
353                         default:
354                             break;
355                     }
356                 }
357                 return true;
358             default:
359                 break;
360         }
361         return super.dispatchKeyEvent(event);
362     }
363 
364     @Override
onBackPressed()365     public void onBackPressed() {
366         // Don't allow back to dismiss. This method is overriden by AlarmAlert
367         // so that the dialog is dismissed.
368         if (LOG) {
369             Log.v("AlarmAlertFullScreen - onBackPressed");
370         }
371         return;
372     }
373 
374 
375     @Override
onGrabbed(View v, int handle)376     public void onGrabbed(View v, int handle) {
377         mPingEnabled = false;
378     }
379 
380     @Override
onReleased(View v, int handle)381     public void onReleased(View v, int handle) {
382         mPingEnabled = true;
383         triggerPing();
384     }
385 
386     @Override
onTrigger(View v, int target)387     public void onTrigger(View v, int target) {
388         final int resId = mGlowPadView.getResourceIdForTarget(target);
389         switch (resId) {
390             case R.drawable.ic_alarm_alert_snooze:
391                 snooze();
392                 break;
393 
394             case R.drawable.ic_alarm_alert_dismiss:
395                 dismiss(false, false);
396                 break;
397             default:
398                 // Code should never reach here.
399                 Log.e("Trigger detected on unhandled resource. Skipping.");
400         }
401     }
402 
403     @Override
onGrabbedStateChange(View v, int handle)404     public void onGrabbedStateChange(View v, int handle) {
405     }
406 
407     @Override
onFinishFinalAnimation()408     public void onFinishFinalAnimation() {
409     }
410 }
411