LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := DeskClock
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.DEVICE_POWER" />
<application android:label="@string/app_label"
android:icon="@drawable/ic_launcher_alarmclock">
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.TIME_SET" />
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
+ <action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter>
</receiver>
<!-- This is the alarm clock in LANDSCAPE desk dock mode.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
>
<LinearLayout
android:id="@+id/desk_clock"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="25dip"
>
android:orientation="vertical"
android:layout_gravity="left"
android:layout_weight="1"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:layout_marginLeft="25dip"
android:layout_marginTop="18dip"
android:layout_marginRight="18dip"
<!-- across the top: next alarm, battery, nightmode button -->
<LinearLayout
android:orientation="horizontal"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_marginBottom="6dip"
<LinearLayout
android:orientation="horizontal"
android:layout_weight="1"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
>
</LinearLayout>
<View android:id="@+id/window_tint"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:background="#E0000000"
android:visibility="visible"
android:clickable="false"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:gravity="center">
<LinearLayout
android:singleLine="true"
android:ellipsize="end"
android:gravity="center"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ImageView
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="1dip"
android:scaleType="fitXY"
android:gravity="fill_horizontal"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/base_layout"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout android:id="@+id/add_alarm"
android:clickable="true"
android:focusable="true"
android:background="@android:drawable/list_selector_background"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
</LinearLayout>
<ImageView
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:gravity="fill_horizontal"
<ListView
android:id="@+id/alarms_list"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
<LinearLayout
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<com.android.deskclock.DigitalClock
style="@style/ButtonStripRight"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
<LinearLayout
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="true">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:src="@*android:drawable/divider_vertical_dark"
android:background="?android:attr/windowBackground"
android:layout_width="wrap_content"
- android:layout_height="fill_parent"
+ android:layout_height="match_parent"
android:paddingTop="4dip"
android:paddingBottom="4dip"
android:scaleType="fitXY"
and an optional line below, used for day/days of week -->
<com.android.deskclock.DigitalClock android:id="@+id/digitalClock"
android:layout_width="wrap_content"
- android:layout_height="fill_parent"
+ android:layout_height="match_parent"
android:gravity="center_vertical"
android:layout_weight="1"
android:orientation="vertical"
android:paddingRight="16dip">
<LinearLayout
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="true">
<TextView android:id="@+id/daysOfWeek"
android:includeFontPadding="false"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"/>
-->
<AnalogClock xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/analog_appwidget"
android:dial="@drawable/appwidget_clock_dial"
android:hand_hour="@drawable/appwidget_clock_hour"
android:hand_minute="@drawable/appwidget_clock_minute"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" />
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="6dip"
android:paddingBottom="9dip"
<TextView android:id="@+id/header_time"
style="?android:attr/textAppearanceLarge"
android:layout_width="wrap_content"
- android:layout_height="fill_parent"
+ android:layout_height="match_parent"
android:singleLine="true"
android:gravity="center_vertical"
android:ellipsize="none"/>
<TextView android:id="@+id/header_label"
style="?android:attr/textAppearanceLarge"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:layout_marginLeft="20dip"
android:singleLine="true"
android:gravity="right|center_vertical"
Buttons are provided to access alarms, music, and other useful functions.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
>
<!-- the top padding accounts for the status bar area -->
<LinearLayout
android:id="@+id/desk_clock"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="25dip"
>
android:orientation="vertical"
android:layout_gravity="left"
android:layout_weight="1"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:layout_marginLeft="22dip"
android:layout_marginTop="18dip"
android:layout_marginRight="18dip"
<!-- across the top: next alarm, nightmode button -->
<LinearLayout
android:orientation="horizontal"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_marginLeft="4dip"
<include layout="@layout/desk_clock_battery"
android:layout_height="wrap_content"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_weight="0"
android:layout_marginLeft="4dip"
/>
</LinearLayout>
<View android:id="@+id/window_tint"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:background="#E0000000"
android:visibility="visible"
android:clickable="false"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"
>
<ImageButton android:id="@+id/alarm_button"
style="@style/ButtonStripLeft"
android:layout_height="wrap_content"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_weight=".25"
android:src="@drawable/ic_clock_strip_alarm"
android:contentDescription="@string/alarm_button_description"
<ImageButton android:id="@+id/gallery_button"
style="@style/ButtonStripMiddle"
android:layout_height="wrap_content"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_weight=".25"
android:src="@drawable/ic_clock_strip_gallery"
android:contentDescription="@string/gallery_button_description"
<ImageButton android:id="@+id/music_button"
style="@style/ButtonStripMiddle"
android:layout_height="wrap_content"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_weight=".25"
android:src="@drawable/ic_clock_strip_music"
android:contentDescription="@string/music_button_description"
<ImageButton android:id="@+id/home_button"
style="@style/ButtonStripRight"
android:layout_height="wrap_content"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_weight=".25"
android:src="@drawable/ic_clock_strip_home"
android:contentDescription="@string/home_button_description"
<!-- Special "screen saver mode" with just the time/date on black. -->
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
>
<View
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:layout_x="0dip"
android:layout_y="0dip"
android:background="#FF000000"
<TextView android:id="@+id/am_pm"
android:layout_width="wrap_content"
- android:layout_height="fill_parent"
+ android:layout_height="match_parent"
android:gravity="bottom"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
style="@android:style/ButtonBar">
<Button android:id="@+id/alarm_save"
android:focusable="true"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/done"/>
<Button android:id="@+id/alarm_cancel"
android:focusable="true"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/revert"/>
<Button android:id="@+id/alarm_delete"
android:focusable="true"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/delete"/>
<resources>
<style name="clock">
- <item name="android:layout_width">fill_parent</item>
- <item name="android:layout_height">fill_parent</item>
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">match_parent</item>
<item name="android:layout_gravity">center_horizontal</item>
</style>
package com.android.deskclock;
-import android.app.Activity;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
+import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
+import android.content.Context;
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;
-import android.view.LayoutInflater;
-import android.view.Window;
+import android.os.Handler;
+import android.os.Message;
import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.Toast;
-import android.widget.TextView;
-
-import java.util.Calendar;
/**
- * Alarm Clock alarm alert: pops visible indicator and plays alarm
- * tone
+ * Full screen alarm alert: pops visible indicator and plays alarm tone. This
+ * activity shows the alert as a dialog.
*/
-public class AlarmAlert extends Activity {
-
- // 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";
+public class AlarmAlert extends AlarmAlertFullScreen {
- private Alarm mAlarm;
- private int mVolumeBehavior;
+ // If we try to check the keyguard more than 5 times, just launch the full
+ // screen activity.
+ private int mKeyguardRetryCount;
+ private final int MAX_KEYGUARD_CHECKS = 5;
- // Receives the ALARM_KILLED action from the AlarmKlaxon.
- private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ private final Handler mHandler = new Handler() {
@Override
- public void onReceive(Context context, Intent intent) {
- Alarm alarm = intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
- if (mAlarm.id == alarm.id) {
- dismiss(true);
- }
+ public void handleMessage(Message msg) {
+ handleScreenOff((KeyguardManager) msg.obj);
}
};
+ private final BroadcastReceiver mScreenOffReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ KeyguardManager km =
+ (KeyguardManager) context.getSystemService(
+ Context.KEYGUARD_SERVICE);
+ handleScreenOff(km);
+ }
+ };
+
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
- 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
- | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
- | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
- updateLayout();
-
- // Register to get the alarm killed intent.
- registerReceiver(mReceiver, new IntentFilter(Alarms.ALARM_KILLED));
- }
-
- private void setTitle() {
- String label = mAlarm.getLabelOrDefault(this);
- TextView title = (TextView) findViewById(R.id.alertTitle);
- title.setText(label);
- }
-
- // 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);
+ // Listen for the screen turning off so that when the screen comes back
+ // on, the user does not need to unlock the phone to dismiss the alarm.
+ registerReceiver(mScreenOffReceiver,
+ new IntentFilter(Intent.ACTION_SCREEN_OFF));
}
- private void updateLayout() {
- LayoutInflater inflater = LayoutInflater.from(this);
-
- setContentView(inflateView(inflater));
-
- /* snooze behavior: pop a snooze confirmation view, kick alarm
- manager. */
- 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(false);
- }
- });
-
- /* Set the title from the passed in alarm */
- setTitle();
- }
-
- // Attempt to snooze this alert.
- private void snooze() {
- 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 * 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.flags |= Notification.FLAG_AUTO_CANCEL
- | Notification.FLAG_ONGOING_EVENT;
- 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();
- stopService(new Intent(Alarms.ALARM_ALERT_ACTION));
- finish();
- }
-
- private NotificationManager getNotificationManager() {
- return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- }
-
- // Dismiss the alarm.
- 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));
- }
- finish();
- }
-
- /**
- * this is called when a second alarm is triggered while a
- * previous alert window is still active.
- */
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
-
- if (Log.LOGV) Log.v("AlarmAlert.OnNewIntent()");
-
- mAlarm = intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
-
- setTitle();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- // Don't hang around.
- finish();
- }
-
@Override
public void onDestroy() {
super.onDestroy();
- if (Log.LOGV) Log.v("AlarmAlert.onDestroy()");
- // No longer care about the alarm being killed.
- unregisterReceiver(mReceiver);
+ unregisterReceiver(mScreenOffReceiver);
+ // Remove any of the keyguard messages just in case
+ mHandler.removeMessages(0);
}
@Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- // Do this on key down to handle a few of the system keys.
- boolean up = event.getAction() == KeyEvent.ACTION_UP;
- switch (event.getKeyCode()) {
- // Volume keys and camera keys dismiss the alarm
- case KeyEvent.KEYCODE_VOLUME_UP:
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- case KeyEvent.KEYCODE_CAMERA:
- case KeyEvent.KEYCODE_FOCUS:
- if (up) {
- switch (mVolumeBehavior) {
- case 1:
- snooze();
- break;
+ public void onBackPressed() {
+ finish();
+ }
- case 2:
- dismiss(false);
- break;
+ private boolean checkRetryCount() {
+ if (mKeyguardRetryCount++ >= MAX_KEYGUARD_CHECKS) {
+ Log.e("Tried to read keyguard status too many times, bailing...");
+ return false;
+ }
+ return true;
+ }
- default:
- break;
- }
- }
- return true;
- default:
- break;
+ private void handleScreenOff(final KeyguardManager km) {
+ if (!km.inKeyguardRestrictedInputMode() && checkRetryCount()) {
+ if (checkRetryCount()) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(0, km), 500);
+ }
+ } else {
+ // Launch the full screen activity but do not turn the screen on.
+ Intent i = new Intent(this, AlarmAlertFullScreen.class);
+ i.putExtra(Alarms.ALARM_INTENT_EXTRA, mAlarm);
+ i.putExtra(SCREEN_OFF, true);
+ startActivity(i);
+ finish();
}
- return super.dispatchKeyEvent(event);
}
}
package com.android.deskclock;
+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;
+import android.view.LayoutInflater;
+import android.view.Window;
import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.Toast;
+import android.widget.TextView;
+
+import java.util.Calendar;
/**
- * Full screen alarm alert: pops visible indicator and plays alarm tone. This
- * activity displays the alert in full screen in order to be secure. The
- * background is the current wallpaper.
+ * Alarm Clock alarm alert: pops visible indicator and plays alarm
+ * tone. This activity is the full screen version which shows over the lock
+ * screen with the wallpaper as the background.
*/
-public class AlarmAlertFullScreen extends AlarmAlert {
+public class AlarmAlertFullScreen extends Activity {
+
+ // 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";
+ protected static final String SCREEN_OFF = "screen_off";
+
+ protected 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 (alarm != null && mAlarm.id == alarm.id) {
+ dismiss(true);
+ }
+ }
+ };
+
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
+
+ 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);
+
+ final Window win = getWindow();
+ win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ // Turn on the screen unless we are being launched from the AlarmAlert
+ // subclass.
+ if (!getIntent().getBooleanExtra(SCREEN_OFF, false)) {
+ win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+ }
+
+ updateLayout();
+
+ // Register to get the alarm killed intent.
+ registerReceiver(mReceiver, new IntentFilter(Alarms.ALARM_KILLED));
+ }
+
+ private void setTitle() {
+ String label = mAlarm.getLabelOrDefault(this);
+ TextView title = (TextView) findViewById(R.id.alertTitle);
+ title.setText(label);
+ }
+
+ private void updateLayout() {
+ LayoutInflater inflater = LayoutInflater.from(this);
+
+ setContentView(inflater.inflate(R.layout.alarm_alert, null));
+
+ /* snooze behavior: pop a snooze confirmation view, kick alarm
+ manager. */
+ 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(false);
+ }
+ });
+
+ /* Set the title from the passed in alarm */
+ setTitle();
+ }
+
+ // Attempt to snooze this alert.
+ private void snooze() {
+ // Do not snooze if the snooze button is disabled.
+ if (!findViewById(R.id.snooze).isEnabled()) {
+ dismiss(false);
+ return;
+ }
+ 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 * snoozeMinutes);
+ Alarms.saveSnoozeAlert(AlarmAlertFullScreen.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.flags |= Notification.FLAG_AUTO_CANCEL
+ | Notification.FLAG_ONGOING_EVENT;
+ 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(AlarmAlertFullScreen.this, displayTime,
+ Toast.LENGTH_LONG).show();
+ stopService(new Intent(Alarms.ALARM_ALERT_ACTION));
+ finish();
+ }
+
+ private NotificationManager getNotificationManager() {
+ return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ }
+
+ // Dismiss the alarm.
+ 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));
+ }
+ finish();
+ }
+
+ /**
+ * this is called when a second alarm is triggered while a
+ * previous alert window is still active.
+ */
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ if (Log.LOGV) Log.v("AlarmAlert.OnNewIntent()");
+
+ mAlarm = intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
+
+ setTitle();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ // If the alarm was deleted at some point, disable snooze.
+ if (Alarms.getAlarm(getContentResolver(), mAlarm.id) == null) {
+ Button snooze = (Button) findViewById(R.id.snooze);
+ snooze.setEnabled(false);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (!isFinishing()) {
+ // Don't hang around.
+ finish();
+ }
}
@Override
+ 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.
+ boolean up = event.getAction() == KeyEvent.ACTION_UP;
+ switch (event.getKeyCode()) {
+ // Volume keys and camera keys dismiss the alarm
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ case KeyEvent.KEYCODE_CAMERA:
+ case KeyEvent.KEYCODE_FOCUS:
+ if (up) {
+ switch (mVolumeBehavior) {
+ case 1:
+ snooze();
+ break;
+
+ case 2:
+ dismiss(false);
+ break;
+
+ default:
+ break;
+ }
+ }
+ return true;
+ default:
+ break;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
public void onBackPressed() {
- // Don't allow back to dismiss.
+ // Don't allow back to dismiss. This method is overriden by AlarmAlert
+ // so that the dialog is dismissed.
return;
}
}
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.provider.Settings;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.View.OnCreateContextMenuListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
*/
public class AlarmClock extends Activity implements OnItemClickListener {
- final static String PREFERENCES = "AlarmClock";
+ static final String PREFERENCES = "AlarmClock";
/** This must be false for production. If true, turns on logging,
test code, etc. */
- final static boolean DEBUG = false;
+ static final boolean DEBUG = false;
private SharedPreferences mPrefs;
private LayoutInflater mFactory;
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View ret = mFactory.inflate(R.layout.alarm_time, parent, false);
- DigitalClock digitalClock = (DigitalClock) ret.findViewById(R.id.digitalClock);
+ DigitalClock digitalClock =
+ (DigitalClock) ret.findViewById(R.id.digitalClock);
digitalClock.setLive(false);
- if (Log.LOGV) Log.v("newView " + cursor.getPosition());
return ret;
}
private static void enableAlarmInternal(final Context context,
final Alarm alarm, boolean enabled) {
+ if (alarm == null) {
+ return;
+ }
ContentResolver resolver = context.getContentResolver();
ContentValues values = new ContentValues(2);
alarm.daysOfWeek).getTimeInMillis();
}
values.put(Alarm.Columns.ALARM_TIME, time);
+ } else {
+ // Clear the snooze if the id matches.
+ disableSnoozeAlert(context, alarm.id);
}
resolver.update(ContentUris.withAppendedId(
// Get the alarm from the db.
final Alarm alarm = getAlarm(context.getContentResolver(), id);
+ if (alarm == null) {
+ return false;
+ }
// The time in the database is either 0 (repeating) or a specific time
// for a non-repeating alarm. Update this value so the AlarmReceiver
// has the right time to compare.
package com.android.deskclock;
-import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.PorterDuff;
-import android.net.Uri;
-import android.provider.Calendar;
-import android.provider.Calendar.Attendees;
-import android.provider.Calendar.Calendars;
-import android.provider.Calendar.EventsColumns;
-import android.provider.Calendar.Instances;
-import android.provider.Calendar.Reminders;
-import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.util.Config;
-import android.util.Log;
-import android.view.View;
import android.widget.RemoteViews;
-import java.util.Arrays;
-
/**
* Simple widget to show analog clock.
*/
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
-
+
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.analog_appwidget);
-
- int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
-
+
+ views.setOnClickPendingIntent(R.id.analog_appwidget,
+ PendingIntent.getActivity(context, 0,
+ new Intent(context, AlarmClock.class),
+ PendingIntent.FLAG_CANCEL_CURRENT));
+
+ int[] appWidgetIds = intent.getIntArrayExtra(
+ AppWidgetManager.EXTRA_APPWIDGET_IDS);
+
AppWidgetManager gm = AppWidgetManager.getInstance(context);
gm.updateAppWidget(appWidgetIds, views);
}
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
// Alarm action for midnight (so we can update the date display).
private static final String ACTION_MIDNIGHT = "com.android.deskclock.MIDNIGHT";
- // Interval between polls of the weather widget. Its refresh period is
- // likely to be much longer (~3h), but we want to pick up any changes
- // within 5 minutes.
- private final long QUERY_WEATHER_DELAY = 5 * 60 * 1000; // 5 min
+ // Interval between forced polls of the weather widget.
+ private final long QUERY_WEATHER_DELAY = 60 * 60 * 1000; // 1 hr
// Delay before engaging the burn-in protection mode (green-on-black).
- private final long SCREEN_SAVER_TIMEOUT = 5* 60 * 1000; // 10 min
+ private final long SCREEN_SAVER_TIMEOUT = 5 * 60 * 1000; // 5 min
// Repositioning delay in screen saver.
private final long SCREEN_SAVER_MOVE_DELAY = 60 * 1000; // 1 min
private boolean mLaunchedFromDock = false;
- private int mIdleTimeoutEpoch = 0;
-
private Random mRNG;
private PendingIntent mMidnightIntent;
} else if (m.what == UPDATE_WEATHER_DISPLAY_MSG) {
updateWeatherDisplay();
} else if (m.what == SCREEN_SAVER_TIMEOUT_MSG) {
- if (m.arg1 == mIdleTimeoutEpoch) {
- saveScreen();
- }
+ saveScreen();
} else if (m.what == SCREEN_SAVER_MOVE_MSG) {
moveScreenSaver();
}
}
};
+ private final ContentObserver mContentObserver = new ContentObserver(mHandy) {
+ @Override
+ public void onChange(boolean selfChange) {
+ if (DEBUG) Log.d(LOG_TAG, "content observer notified that weather changed");
+ refreshWeather();
+ }
+ };
+
private void moveScreenSaver() {
moveScreenSaverTo(-1,-1);
win.setAttributes(winParams);
}
+ private void scheduleScreenSaver() {
+ // reschedule screen saver
+ mHandy.removeMessages(SCREEN_SAVER_TIMEOUT_MSG);
+ mHandy.sendMessageDelayed(
+ Message.obtain(mHandy, SCREEN_SAVER_TIMEOUT_MSG),
+ SCREEN_SAVER_TIMEOUT);
+ }
+
private void restoreScreen() {
if (!mScreenSaverMode) return;
if (DEBUG) Log.d(LOG_TAG, "restoreScreen");
doDim(false); // restores previous dim mode
// policy: update weather info when returning from screen saver
if (mPluggedIn) requestWeatherDataFetch();
+
+ scheduleScreenSaver();
+
refreshAll();
}
private void requestWeatherDataFetch() {
if (DEBUG) Log.d(LOG_TAG, "forcing the Genie widget to update weather now...");
sendBroadcast(new Intent(ACTION_GENIE_REFRESH).putExtra("requestWeather", true));
- // update the display with any new data
- scheduleWeatherQueryDelayed(5000);
+ // we expect the result to show up in our content observer
}
private boolean supportsWeather() {
mWeatherIconDrawable = mGenieResources.getDrawable(cur.getInt(
cur.getColumnIndexOrThrow("iconResId")));
-
+ mWeatherCurrentTemperatureString = String.format("%d\u00b0",
+ (cur.getInt(cur.getColumnIndexOrThrow("temperature"))));
+ mWeatherHighTemperatureString = String.format("%d\u00b0",
+ (cur.getInt(cur.getColumnIndexOrThrow("highTemperature"))));
+ mWeatherLowTemperatureString = String.format("%d\u00b0",
+ (cur.getInt(cur.getColumnIndexOrThrow("lowTemperature"))));
mWeatherLocationString = cur.getString(
cur.getColumnIndexOrThrow("location"));
-
- // any of these may be NULL
- final int colTemp = cur.getColumnIndexOrThrow("temperature");
- final int colHigh = cur.getColumnIndexOrThrow("highTemperature");
- final int colLow = cur.getColumnIndexOrThrow("lowTemperature");
-
- mWeatherCurrentTemperatureString =
- cur.isNull(colTemp)
- ? "\u2014"
- : String.format("%d\u00b0", cur.getInt(colTemp));
- mWeatherHighTemperatureString =
- cur.isNull(colHigh)
- ? "\u2014"
- : String.format("%d\u00b0", cur.getInt(colHigh));
- mWeatherLowTemperatureString =
- cur.isNull(colLow)
- ? "\u2014"
- : String.format("%d\u00b0", cur.getInt(colLow));
} else {
Log.w(LOG_TAG, "No weather information available (cur="
+ cur +")");
mWeatherIconDrawable = null;
+ mWeatherHighTemperatureString = "";
+ mWeatherLowTemperatureString = "";
mWeatherLocationString = getString(R.string.weather_fetch_failure);
- mWeatherCurrentTemperatureString =
- mWeatherHighTemperatureString =
- mWeatherLowTemperatureString = "";
}
mHandy.sendEmptyMessage(UPDATE_WEATHER_DISPLAY_MSG);
filter.addAction(ACTION_MIDNIGHT);
registerReceiver(mIntentReceiver, filter);
+ // Listen for updates to weather data
+ Uri weatherNotificationUri = new Uri.Builder()
+ .scheme(android.content.ContentResolver.SCHEME_CONTENT)
+ .authority(WEATHER_CONTENT_AUTHORITY)
+ .path(WEATHER_CONTENT_PATH)
+ .build();
+ getContentResolver().registerContentObserver(
+ weatherNotificationUri, true, mContentObserver);
+
+ // Elaborate mechanism to find out when the day rolls over
Calendar today = Calendar.getInstance();
today.set(Calendar.HOUR_OF_DAY, 0);
today.set(Calendar.MINUTE, 0);
setWakeLock(mPluggedIn);
- mIdleTimeoutEpoch++;
- mHandy.sendMessageDelayed(
- Message.obtain(mHandy, SCREEN_SAVER_TIMEOUT_MSG, mIdleTimeoutEpoch, 0),
- SCREEN_SAVER_TIMEOUT);
+ scheduleScreenSaver();
final boolean launchedFromDock
= getIntent().hasCategory(Intent.CATEGORY_DESK_DOCK);
public void onPause() {
if (DEBUG) Log.d(LOG_TAG, "onPause");
- // Turn off the screen saver. (But don't un-dim.)
+ // Turn off the screen saver and cancel any pending timeouts.
+ // (But don't un-dim.)
+ mHandy.removeMessages(SCREEN_SAVER_TIMEOUT_MSG);
restoreScreen();
// Other things we don't want to be doing in the background.
unregisterReceiver(mIntentReceiver);
+ getContentResolver().unregisterContentObserver(mContentObserver);
+
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.cancel(mMidnightIntent);
unscheduleWeatherQuery();
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.media.RingtoneManager;
-import android.net.Uri;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
/* load alarm details from database */
Alarm alarm = Alarms.getAlarm(getContentResolver(), mId);
+ // Bad alarm, bail to avoid a NPE.
+ if (alarm == null) {
+ finish();
+ return;
+ }
mEnabledPref.setChecked(alarm.enabled);
mLabel.setText(alarm.label);
mLabel.setSummary(alarm.label);
// Have the ListView expand to fill the screen minus the save/cancel
// buttons.
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- LayoutParams.FILL_PARENT,
+ LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
lp.weight = 1;
ll.addView(lv, lp);
Button b = (Button) v.findViewById(R.id.alarm_save);
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
+ // Enable the alarm when clicking "Done"
+ mEnabledPref.setChecked(true);
saveAlarm();
finish();
}