summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMao Jinlong <c_jmao@codeaurora.org>2015-10-28 15:18:36 +0800
committerSteve Kondik <steve@cyngn.com>2015-11-30 19:32:13 -0800
commit81e6b54aa2dbba531b4a17f512c44d883db63dd4 (patch)
tree1c3c321df899ae40d08a113d5de963814a0f4d4a
parent78cc04628091f8d7818ca5e1b2f883efe8731918 (diff)
downloadandroid_packages_apps_DeskClock-81e6b54aa2dbba531b4a17f512c44d883db63dd4.tar.gz
android_packages_apps_DeskClock-81e6b54aa2dbba531b4a17f512c44d883db63dd4.tar.bz2
android_packages_apps_DeskClock-81e6b54aa2dbba531b4a17f512c44d883db63dd4.zip
DeskClock: add support for power off alarm
When it is alarm boot, it will trigger power off alarm alert view at the very beginning of the phone boot phase. During power off alarm alert view: - keyguard is dismissed, will restore later - user can choose to continue power on the device or not Change-Id: Id5bce67793cef694712b0591be68ef6882be6693
-rw-r--r--Android.mk3
-rw-r--r--AndroidManifest.xml10
-rw-r--r--res/values-zh-rCN/strings.xml3
-rw-r--r--res/values/strings.xml3
-rw-r--r--src/com/android/deskclock/alarms/AlarmActivity.java204
-rw-r--r--src/com/android/deskclock/alarms/AlarmService.java4
-rw-r--r--src/com/android/deskclock/provider/AlarmInstance.java28
7 files changed, 230 insertions, 25 deletions
diff --git a/Android.mk b/Android.mk
index bd6c66302..a92fdf14f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -15,6 +15,9 @@ endif
LOCAL_MODULE_TAGS := optional
LOCAL_PACKAGE_NAME := DeskClock
+
+LOCAL_CERTIFICATE := platform
+
LOCAL_OVERRIDES_PACKAGES := AlarmClock
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a66b5c50a..5e9611f36 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -35,7 +35,7 @@
<!-- READ_EXTERNAL_STORAGE is required to play custom ringtones from the SD card prior to M -->
<!-- It is also required to display user-friendly names for custom ringtones in the UI. -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-
+ <uses-permission android:name="android.permission.SHUTDOWN" />
<application android:label="@string/app_label"
android:name=".DeskClockApplication"
android:allowBackup="true"
@@ -105,7 +105,13 @@
android:excludeFromRecents="true"
android:theme="@style/AlarmAlertFullScreenTheme"
android:windowSoftInputMode="stateAlwaysHidden"
- android:showOnLockScreen="true" />
+ android:showOnLockScreen="true"
+ >
+ <intent-filter>
+ <action android:name="org.codeaurora.alarm.action.POWER_OFF_ALARM" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
<activity android:name="ScreensaverActivity"
android:excludeFromRecents="true"
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 03c00f98d..5999a5e08 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -400,4 +400,7 @@
<string name="pick_alarm_to_dismiss" msgid="5408769235866082896">"请选择要关闭的闹钟"</string>
<string name="no_firing_alarms" msgid="4986161963178722289">"目前没有正在响铃的闹钟"</string>
<string name="alarm_is_snoozed" msgid="7044644119744928846">"<xliff:g id="ALARM_TIME">%s</xliff:g> 的闹钟已延后 10 分钟"</string>
+ <string name="power_on_text">"开机"</string>
+ <string name="power_on_yes_text">"是"</string>
+ <string name="power_on_no_text">"否"</string>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4f2814d2f..036d03297 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1135,5 +1135,8 @@
-->
<string name="alarm_is_snoozed"><xliff:g id="alarm_time" example="14:20">%s</xliff:g> alarm snoozed for 10 minutes</string>
+ <string name="power_on_text">Power on</string>
+ <string name="power_on_yes_text">Yes</string>
+ <string name="power_on_no_text">No</string>
</resources>
diff --git a/src/com/android/deskclock/alarms/AlarmActivity.java b/src/com/android/deskclock/alarms/AlarmActivity.java
index ebfa0ea8e..69abb56c3 100644
--- a/src/com/android/deskclock/alarms/AlarmActivity.java
+++ b/src/com/android/deskclock/alarms/AlarmActivity.java
@@ -23,9 +23,11 @@ import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
+import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
@@ -33,11 +35,15 @@ import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
import android.preference.PreferenceManager;
+import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.graphics.ColorUtils;
import android.support.v4.view.animation.PathInterpolatorCompat;
@@ -66,6 +72,13 @@ public class AlarmActivity extends AppCompatActivity
private static final String LOGTAG = AlarmActivity.class.getSimpleName();
+ private static final String POWER_OFF_ALARM = "powerOffAlarm";
+
+ private static final String POWER_OFF_ALARM_MODE = "power_off_alarm_mode";
+
+ private static final String ACTION_POWER_OFF_ALARM =
+ "org.codeaurora.alarm.action.POWER_OFF_ALARM";
+
private static final TimeInterpolator PULSE_INTERPOLATOR =
PathInterpolatorCompat.create(0.4f, 0.0f, 0.2f, 1.0f);
private static final TimeInterpolator REVEAL_INTERPOLATOR =
@@ -80,6 +93,35 @@ public class AlarmActivity extends AppCompatActivity
private static final float BUTTON_SCALE_DEFAULT = 0.7f;
private static final int BUTTON_DRAWABLE_ALPHA_DEFAULT = 165;
+ public static boolean mIsPowerOffAlarm = false;
+
+ private static final int SHUTDOWN_ALARM_VIEW = 1;
+ private static final int SHUTDOWN_POWER_OFF = 2;
+
+ private Context mContext;
+
+ private boolean mIsSoonze = false;
+ private boolean mIsPowerOffing = false;
+
+ private Handler mBootHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case SHUTDOWN_ALARM_VIEW:
+ LogUtils.v(LOGTAG, "SHUTDOWN_ALARM_VIEW finish before sleep 500ms");
+ finish();
+ break;
+
+ case SHUTDOWN_POWER_OFF:
+ LogUtils.v(LOGTAG, "SHUTDOWN_POWER_OFF directly power off");
+ powerOff();
+ break;
+
+ default:// normally will not go here
+ }
+ }
+ };
+
private final Handler mHandler = new Handler();
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -96,7 +138,9 @@ public class AlarmActivity extends AppCompatActivity
dismiss();
break;
case AlarmService.ALARM_DONE_ACTION:
- finish();
+ if (!mIsPowerOffAlarm || mIsSoonze) {
+ finish();
+ }
break;
default:
LogUtils.i(LOGTAG, "Unknown broadcast: %s", action);
@@ -149,14 +193,31 @@ public class AlarmActivity extends AppCompatActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- final long instanceId = AlarmInstance.getId(getIntent().getData());
- mAlarmInstance = AlarmInstance.getInstance(getContentResolver(), instanceId);
+ Uri intentData = getIntent().getData();
+ String intentAction = getIntent().getAction();
+
+ if (intentAction == ACTION_POWER_OFF_ALARM) {
+ mIsPowerOffAlarm = true;
+ }
+
+ mContext = getApplicationContext();
+
+ if (mIsPowerOffAlarm) {
+ mAlarmInstance = AlarmInstance.getFirstAlarmInstance(mContext.getContentResolver());
+
+ Settings.System.putInt(mContext.getContentResolver(), POWER_OFF_ALARM_MODE, 1);
+
+ } else if (intentData != null) {
+ long instanceId = AlarmInstance.getId(intentData);
+ mAlarmInstance = AlarmInstance.getInstance(this.getContentResolver(), instanceId);
+ }
+
if (mAlarmInstance == null) {
// The alarm was deleted before the activity got created, so just finish()
LogUtils.e(LOGTAG, "Error displaying alarm for intent: %s", getIntent());
finish();
return;
- } else if (mAlarmInstance.mAlarmState != AlarmInstance.FIRED_STATE) {
+ } else if (!mIsPowerOffAlarm && mAlarmInstance.mAlarmState != AlarmInstance.FIRED_STATE) {
LogUtils.i(LOGTAG, "Skip displaying alarm for instance: %s", mAlarmInstance);
finish();
return;
@@ -169,11 +230,21 @@ public class AlarmActivity extends AppCompatActivity
.getString(SettingsActivity.KEY_VOLUME_BEHAVIOR,
SettingsActivity.DEFAULT_VOLUME_BEHAVIOR);
- 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
- | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON);
+ if (mIsPowerOffAlarm) {
+ getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY);
+ 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
+ | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ } else {
+ 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
+ | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON);
+ }
// Hide navigation bar to minimize accidental tap on Home key
hideNavigationBar();
@@ -231,6 +302,10 @@ public class AlarmActivity extends AppCompatActivity
mPulseAnimator.setInterpolator(PULSE_INTERPOLATOR);
mPulseAnimator.setRepeatCount(ValueAnimator.INFINITE);
mPulseAnimator.start();
+
+ if (mAlarmInstance != null && mIsPowerOffAlarm) {
+ AlarmStateManager.setFiredState(getApplicationContext(), mAlarmInstance);
+ }
}
@Override
@@ -246,21 +321,30 @@ public class AlarmActivity extends AppCompatActivity
protected void onResume() {
super.onResume();
- // Re-query for AlarmInstance in case the state has changed externally
- final long instanceId = AlarmInstance.getId(getIntent().getData());
- mAlarmInstance = AlarmInstance.getInstance(getContentResolver(), instanceId);
+ Uri intentData = getIntent().getData();
- if (mAlarmInstance == null) {
- LogUtils.i(LOGTAG, "No alarm instance for instanceId: %d", instanceId);
- finish();
- return;
+ String intentAction = getIntent().getAction();
+ if (intentAction == ACTION_POWER_OFF_ALARM) {
+ mIsPowerOffAlarm = true;
}
- // Verify that the alarm is still firing before showing the activity
- if (mAlarmInstance.mAlarmState != AlarmInstance.FIRED_STATE) {
- LogUtils.i(LOGTAG, "Skip displaying alarm for instance: %s", mAlarmInstance);
- finish();
- return;
+ if(!mIsPowerOffAlarm && intentData != null) {
+ // Re-query for AlarmInstance in case the state has changed externally
+ final long instanceId = AlarmInstance.getId(getIntent().getData());
+ mAlarmInstance = AlarmInstance.getInstance(getContentResolver(), instanceId);
+
+ if (mAlarmInstance == null) {
+ LogUtils.i(LOGTAG, "No alarm instance for instanceId: %d", instanceId);
+ finish();
+ return;
+ }
+
+ // Verify that the alarm is still firing before showing the activity
+ if (!mIsPowerOffAlarm && mAlarmInstance.mAlarmState != AlarmInstance.FIRED_STATE) {
+ LogUtils.i(LOGTAG, "Skip displaying alarm for instance: %s", mAlarmInstance);
+ finish();
+ return;
+ }
}
if (!mReceiverRegistered) {
@@ -286,6 +370,24 @@ public class AlarmActivity extends AppCompatActivity
unregisterReceiver(mReceiver);
mReceiverRegistered = false;
}
+
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mIsPowerOffAlarm) {
+ // Boot alarm should not be destroyed before being handled.
+ if (!mIsPowerOffing) {
+ if (!mAlarmHandled) {
+ Settings.System.putInt(mContext.getContentResolver(), POWER_OFF_ALARM_MODE, 0);
+ mIsPowerOffAlarm = false;
+ LogUtils.d(LOGTAG, "onDestroy setSnoozeState = " + mAlarmInstance);
+ AlarmStateManager.setSnoozeState(this, mAlarmInstance, false );
+ }
+ }
+ }
+
}
@Override
@@ -459,6 +561,8 @@ public class AlarmActivity extends AppCompatActivity
* Perform snooze animation and send snooze intent.
*/
private void snooze() {
+ mIsSoonze = true;
+
mAlarmHandled = true;
LogUtils.v(LOGTAG, "Snoozed: %s", mAlarmInstance);
@@ -501,6 +605,10 @@ public class AlarmActivity extends AppCompatActivity
// Unbind here, otherwise alarm will keep ringing until activity finishes.
unbindAlarmService();
+
+ if (mIsPowerOffAlarm) {
+ showPowerOffDialog();
+ }
}
/**
@@ -616,7 +724,9 @@ public class AlarmActivity extends AppCompatActivity
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
- finish();
+ if ((!mIsPowerOffAlarm && !mIsPowerOffing) || mIsSoonze) {
+ finish();
+ }
}
}, ALERT_DISMISS_DELAY_MILLIS);
}
@@ -624,4 +734,54 @@ public class AlarmActivity extends AppCompatActivity
return alertAnimator;
}
+
+ /**
+ * Implement power off function immediately.
+ */
+ private void powerOff() {
+ Intent requestShutdown = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
+ requestShutdown.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
+ requestShutdown.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(requestShutdown);
+ }
+
+ private void showPowerOffDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setMessage(R.string.power_on_text)
+ .setTitle(R.string.alarm_list_title);
+ builder.setPositiveButton(R.string.power_on_yes_text,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Settings.System.putInt(mContext.getContentResolver(),
+ POWER_OFF_ALARM_MODE, 0);
+ mIsPowerOffAlarm = false;
+ mBootHandler.sendEmptyMessage(SHUTDOWN_ALARM_VIEW);
+ }
+ });
+ builder.setNegativeButton(R.string.power_on_no_text,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Settings.System.putInt(mContext.getContentResolver(),
+ POWER_OFF_ALARM_MODE, 0);
+ mIsPowerOffAlarm = false;
+ mIsPowerOffing = true;
+ mBootHandler.sendEmptyMessage(SHUTDOWN_POWER_OFF);
+ }
+ });
+
+ AlertDialog poweroffDialog = builder.create();
+ poweroffDialog.setCancelable(false);
+ poweroffDialog.setCanceledOnTouchOutside(false);
+ poweroffDialog.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 |
+ WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON |
+ WindowManager.LayoutParams.FLAG_FULLSCREEN |
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
+ poweroffDialog.show();
+ }
+
}
diff --git a/src/com/android/deskclock/alarms/AlarmService.java b/src/com/android/deskclock/alarms/AlarmService.java
index 1e0c88731..a75b7f652 100644
--- a/src/com/android/deskclock/alarms/AlarmService.java
+++ b/src/com/android/deskclock/alarms/AlarmService.java
@@ -147,7 +147,9 @@ public class AlarmService extends Service {
Events.sendEvent(R.string.category_alarm, R.string.action_fire, 0);
mCurrentAlarm = instance;
- AlarmNotifications.showAlarmNotification(this, mCurrentAlarm);
+ if(!AlarmActivity.mIsPowerOffAlarm) {
+ AlarmNotifications.showAlarmNotification(this, mCurrentAlarm);
+ }
mInitialCallState = mTelephonyManager.getCallState();
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
AlarmKlaxon.start(this, mCurrentAlarm);
diff --git a/src/com/android/deskclock/provider/AlarmInstance.java b/src/com/android/deskclock/provider/AlarmInstance.java
index 667e1fb38..8619b8491 100644
--- a/src/com/android/deskclock/provider/AlarmInstance.java
+++ b/src/com/android/deskclock/provider/AlarmInstance.java
@@ -302,6 +302,34 @@ public final class AlarmInstance implements ClockContract.InstancesColumns {
}
}
+ /**
+ * Get first alarm instance of power off alarm which is the closest missed alarm.
+ *
+ * @param contentResolver to access the content provider
+ */
+ public static AlarmInstance getFirstAlarmInstance(ContentResolver contentResolver) {
+ List<AlarmInstance> alertAlarms = getInstances(contentResolver, null);
+ long currentTime = System.currentTimeMillis();
+
+ AlarmInstance firstAlarm = null;
+ long closestMissAlarmElapse = currentTime;
+
+ for (AlarmInstance ai : alertAlarms) {
+ long time = currentTime - ai.getAlarmTime().getTimeInMillis();
+
+ if (time < 0) {
+ continue;
+ }
+
+ if (closestMissAlarmElapse > time) {
+ firstAlarm = ai;
+ closestMissAlarmElapse = time;
+ }
+ }
+
+ return firstAlarm;
+ }
+
// Public fields
public long mId;
public int mYear;