Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 81e6b54a authored by Mao Jinlong's avatar Mao Jinlong Committed by Steve Kondik
Browse files

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
parent 78cc0462
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -15,6 +15,9 @@ endif
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_TAGS := optional


LOCAL_PACKAGE_NAME := DeskClock
LOCAL_PACKAGE_NAME := DeskClock

LOCAL_CERTIFICATE := platform

LOCAL_OVERRIDES_PACKAGES := AlarmClock
LOCAL_OVERRIDES_PACKAGES := AlarmClock


LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+8 −2
Original line number Original line Diff line number Diff line
@@ -35,7 +35,7 @@
    <!-- READ_EXTERNAL_STORAGE is required to play custom ringtones from the SD card prior to M -->
    <!-- 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. -->
    <!-- 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.READ_EXTERNAL_STORAGE" />

    <uses-permission android:name="android.permission.SHUTDOWN" />
    <application android:label="@string/app_label"
    <application android:label="@string/app_label"
                 android:name=".DeskClockApplication"
                 android:name=".DeskClockApplication"
                 android:allowBackup="true"
                 android:allowBackup="true"
@@ -105,7 +105,13 @@
                android:excludeFromRecents="true"
                android:excludeFromRecents="true"
                android:theme="@style/AlarmAlertFullScreenTheme"
                android:theme="@style/AlarmAlertFullScreenTheme"
                android:windowSoftInputMode="stateAlwaysHidden"
                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"
        <activity android:name="ScreensaverActivity"
                android:excludeFromRecents="true"
                android:excludeFromRecents="true"
+3 −0
Original line number Original line Diff line number Diff line
@@ -400,4 +400,7 @@
    <string name="pick_alarm_to_dismiss" msgid="5408769235866082896">"请选择要关闭的闹钟"</string>
    <string name="pick_alarm_to_dismiss" msgid="5408769235866082896">"请选择要关闭的闹钟"</string>
    <string name="no_firing_alarms" msgid="4986161963178722289">"目前没有正在响铃的闹钟"</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="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>
</resources>
+3 −0
Original line number Original line Diff line number Diff line
@@ -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="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>
</resources>
+182 −22
Original line number Original line Diff line number Diff line
@@ -23,9 +23,11 @@ import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.ServiceConnection;
@@ -33,11 +35,15 @@ import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Build;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.NonNull;
import android.support.v4.graphics.ColorUtils;
import android.support.v4.graphics.ColorUtils;
import android.support.v4.view.animation.PathInterpolatorCompat;
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 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 =
    private static final TimeInterpolator PULSE_INTERPOLATOR =
            PathInterpolatorCompat.create(0.4f, 0.0f, 0.2f, 1.0f);
            PathInterpolatorCompat.create(0.4f, 0.0f, 0.2f, 1.0f);
    private static final TimeInterpolator REVEAL_INTERPOLATOR =
    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 float BUTTON_SCALE_DEFAULT = 0.7f;
    private static final int BUTTON_DRAWABLE_ALPHA_DEFAULT = 165;
    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 Handler mHandler = new Handler();
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        @Override
@@ -96,7 +138,9 @@ public class AlarmActivity extends AppCompatActivity
                        dismiss();
                        dismiss();
                        break;
                        break;
                    case AlarmService.ALARM_DONE_ACTION:
                    case AlarmService.ALARM_DONE_ACTION:
                        if (!mIsPowerOffAlarm || mIsSoonze) {
                            finish();
                            finish();
                        }
                        break;
                        break;
                    default:
                    default:
                        LogUtils.i(LOGTAG, "Unknown broadcast: %s", action);
                        LogUtils.i(LOGTAG, "Unknown broadcast: %s", action);
@@ -149,14 +193,31 @@ public class AlarmActivity extends AppCompatActivity
    protected void onCreate(Bundle savedInstanceState) {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.onCreate(savedInstanceState);


        final long instanceId = AlarmInstance.getId(getIntent().getData());
        Uri intentData = getIntent().getData();
        mAlarmInstance = AlarmInstance.getInstance(getContentResolver(), instanceId);
        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) {
        if (mAlarmInstance == null) {
            // The alarm was deleted before the activity got created, so just finish()
            // The alarm was deleted before the activity got created, so just finish()
            LogUtils.e(LOGTAG, "Error displaying alarm for intent: %s", getIntent());
            LogUtils.e(LOGTAG, "Error displaying alarm for intent: %s", getIntent());
            finish();
            finish();
            return;
            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);
            LogUtils.i(LOGTAG, "Skip displaying alarm for instance: %s", mAlarmInstance);
            finish();
            finish();
            return;
            return;
@@ -169,11 +230,21 @@ public class AlarmActivity extends AppCompatActivity
                .getString(SettingsActivity.KEY_VOLUME_BEHAVIOR,
                .getString(SettingsActivity.KEY_VOLUME_BEHAVIOR,
                        SettingsActivity.DEFAULT_VOLUME_BEHAVIOR);
                        SettingsActivity.DEFAULT_VOLUME_BEHAVIOR);


        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
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                    | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
                    | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                    | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON);
                    | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON);
        }


        // Hide navigation bar to minimize accidental tap on Home key
        // Hide navigation bar to minimize accidental tap on Home key
        hideNavigationBar();
        hideNavigationBar();
@@ -231,6 +302,10 @@ public class AlarmActivity extends AppCompatActivity
        mPulseAnimator.setInterpolator(PULSE_INTERPOLATOR);
        mPulseAnimator.setInterpolator(PULSE_INTERPOLATOR);
        mPulseAnimator.setRepeatCount(ValueAnimator.INFINITE);
        mPulseAnimator.setRepeatCount(ValueAnimator.INFINITE);
        mPulseAnimator.start();
        mPulseAnimator.start();

        if (mAlarmInstance != null && mIsPowerOffAlarm) {
            AlarmStateManager.setFiredState(getApplicationContext(), mAlarmInstance);
        }
    }
    }


    @Override
    @Override
@@ -246,6 +321,14 @@ public class AlarmActivity extends AppCompatActivity
    protected void onResume() {
    protected void onResume() {
        super.onResume();
        super.onResume();


        Uri intentData = getIntent().getData();

        String intentAction = getIntent().getAction();
        if (intentAction == ACTION_POWER_OFF_ALARM) {
            mIsPowerOffAlarm = true;
        }

        if(!mIsPowerOffAlarm && intentData != null) {
            // Re-query for AlarmInstance in case the state has changed externally
            // Re-query for AlarmInstance in case the state has changed externally
            final long instanceId = AlarmInstance.getId(getIntent().getData());
            final long instanceId = AlarmInstance.getId(getIntent().getData());
            mAlarmInstance = AlarmInstance.getInstance(getContentResolver(), instanceId);
            mAlarmInstance = AlarmInstance.getInstance(getContentResolver(), instanceId);
@@ -257,11 +340,12 @@ public class AlarmActivity extends AppCompatActivity
            }
            }


            // Verify that the alarm is still firing before showing the activity
            // Verify that the alarm is still firing before showing the activity
        if (mAlarmInstance.mAlarmState != AlarmInstance.FIRED_STATE) {
            if (!mIsPowerOffAlarm && mAlarmInstance.mAlarmState != AlarmInstance.FIRED_STATE) {
                LogUtils.i(LOGTAG, "Skip displaying alarm for instance: %s", mAlarmInstance);
                LogUtils.i(LOGTAG, "Skip displaying alarm for instance: %s", mAlarmInstance);
                finish();
                finish();
                return;
                return;
            }
            }
        }


        if (!mReceiverRegistered) {
        if (!mReceiverRegistered) {
            // Register to get the alarm done/snooze/dismiss intent.
            // Register to get the alarm done/snooze/dismiss intent.
@@ -286,6 +370,24 @@ public class AlarmActivity extends AppCompatActivity
            unregisterReceiver(mReceiver);
            unregisterReceiver(mReceiver);
            mReceiverRegistered = false;
            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
    @Override
@@ -459,6 +561,8 @@ public class AlarmActivity extends AppCompatActivity
     * Perform snooze animation and send snooze intent.
     * Perform snooze animation and send snooze intent.
     */
     */
    private void snooze() {
    private void snooze() {
        mIsSoonze = true;

        mAlarmHandled = true;
        mAlarmHandled = true;
        LogUtils.v(LOGTAG, "Snoozed: %s", mAlarmInstance);
        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.
        // Unbind here, otherwise alarm will keep ringing until activity finishes.
        unbindAlarmService();
        unbindAlarmService();

        if (mIsPowerOffAlarm) {
            showPowerOffDialog();
        }
    }
    }


    /**
    /**
@@ -616,12 +724,64 @@ public class AlarmActivity extends AppCompatActivity
                mHandler.postDelayed(new Runnable() {
                mHandler.postDelayed(new Runnable() {
                    @Override
                    @Override
                    public void run() {
                    public void run() {
                        if ((!mIsPowerOffAlarm && !mIsPowerOffing) || mIsSoonze) {
                            finish();
                            finish();
                        }
                        }
                    }
                }, ALERT_DISMISS_DELAY_MILLIS);
                }, ALERT_DISMISS_DELAY_MILLIS);
            }
            }
        });
        });


        return alertAnimator;
        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();
    }

}
}
Loading