AmbientDisplay: Add always on prototype

Test: adb shell settings put secure doze_always_on 1
Change-Id: I3f293b3ef43847b7848af416b44f212fc40514d4
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 03e0ebf..68b7b3a 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6001,6 +6001,12 @@
         public static final String DOZE_ENABLED = "doze_enabled";
 
         /**
+         * Whether doze should be always on.
+         * @hide
+         */
+        public static final String DOZE_ALWAYS_ON = "doze_always_on";
+
+        /**
          * Whether the device should pulse on pick up gesture.
          * @hide
          */
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1b7a922..d8c8b82 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1667,6 +1667,14 @@
          not appear on production builds ever. -->
     <string name="plugins" translatable="false">Plugins</string>
 
+    <!-- Ambient display section of the tuner. Non-translatable since it should
+        not appear on production builds ever. -->
+    <string name="tuner_doze" translatable="false">Ambient Display</string>
+
+    <!-- Ambient display always-on of the tuner. Non-translatable since it should
+        not appear on production builds ever. -->
+    <string name="tuner_doze_always_on" translatable="false">Always on</string>
+
     <!-- PIP section of the tuner. Non-translatable since it should
         not appear on production builds ever. -->
     <string name="picture_in_picture" translatable="false">Picture-in-Picture</string>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 46a9ee3..942f847 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -139,6 +139,17 @@
 
     </PreferenceScreen>
 
+    <PreferenceScreen
+      android:key="doze"
+      android:title="@string/tuner_doze">
+
+        <com.android.systemui.tuner.TunerSwitch
+          android:key="doze_always_on"
+          android:title="@string/tuner_doze_always_on"
+          sysui:defValue="false" />
+
+    </PreferenceScreen>
+
     <!--
     <Preference
         android:key="nav_bar"
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 82a1bfe..02a98b0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -27,6 +27,7 @@
     void startDozing(@NonNull Runnable ready);
     void pulseWhileDozing(@NonNull PulseCallback callback, int reason);
     void stopDozing();
+    void dozeTimeTick();
     boolean isPowerSaveActive();
     boolean isNotificationLightOn();
     boolean isPulsingBlocked();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 8789845c..cbaf232 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.doze;
 
+import android.app.AlarmManager;
 import android.app.UiModeManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -43,7 +44,9 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Calendar;
 import java.util.Date;
+import java.util.GregorianCalendar;
 
 public class DozeService extends DreamService implements DozeSensors.Callback {
     private static final String TAG = "DozeService";
@@ -77,6 +80,7 @@
     private long mNotificationPulseTime;
 
     private AmbientDisplayConfiguration mConfig;
+    private AlarmManager mAlarmManager;
 
     public DozeService() {
         if (DEBUG) Log.d(mTag, "new DozeService()");
@@ -114,6 +118,7 @@
         setWindowless(true);
 
         mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+        mAlarmManager = (AlarmManager) mContext.getSystemService(AlarmManager.class);
         mConfig = new AmbientDisplayConfiguration(mContext);
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
@@ -168,6 +173,10 @@
                 // stopDozing because can we just keep dozing until the bitter end.
             }
         }));
+
+        if (mDozeParameters.getAlwaysOn()) {
+            mTimeTick.onAlarm();
+        }
     }
 
     @Override
@@ -184,6 +193,7 @@
 
         // Tell the host that it's over.
         mHost.stopDozing();
+        mAlarmManager.cancel(mTimeTick);
     }
 
     private void requestPulse(final int reason) {
@@ -264,7 +274,11 @@
 
     private void turnDisplayOff() {
         if (DEBUG) Log.d(mTag, "Display off");
-        setDozeScreenState(Display.STATE_OFF);
+        if (mDozeParameters.getAlwaysOn()) {
+            turnDisplayOn();
+        } else {
+            setDozeScreenState(Display.STATE_OFF);
+        }
     }
 
     private void turnDisplayOn() {
@@ -353,6 +367,26 @@
         }
     };
 
+    private AlarmManager.OnAlarmListener mTimeTick = new AlarmManager.OnAlarmListener() {
+        @Override
+        public void onAlarm() {
+            mHost.dozeTimeTick();
+
+            // Keep wakelock until a frame has been pushed.
+            mHandler.post(mWakeLock.wrap(()->{}));
+
+            Calendar calendar = GregorianCalendar.getInstance();
+            calendar.setTimeInMillis(System.currentTimeMillis());
+            calendar.set(Calendar.MILLISECOND, 0);
+            calendar.set(Calendar.SECOND, 0);
+            calendar.add(Calendar.MINUTE, 1);
+
+            long delta = calendar.getTimeInMillis() - System.currentTimeMillis();
+            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    SystemClock.elapsedRealtime() + delta, "doze_time_tick", mTimeTick, mHandler);
+        }
+    };
+
     private final DozeHost.Callback mHostCallback = new DozeHost.Callback() {
         @Override
         public void onNewNotifications() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 9e9bdd7..0e074c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -17,20 +17,17 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.os.Build;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
-import android.util.Log;
 import android.util.MathUtils;
 import android.util.SparseBooleanArray;
 
-import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.systemui.R;
 
 import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 public class DozeParameters {
     private static final int MAX_DURATION = 60 * 1000;
@@ -115,6 +112,12 @@
         return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold);
     }
 
+    public boolean getAlwaysOn() {
+        return Build.IS_DEBUGGABLE
+                && Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                Settings.Secure.DOZE_ALWAYS_ON, 0, UserHandle.USER_CURRENT) != 0;
+    }
+
     private boolean getBoolean(String propName, int resId) {
         return SystemProperties.getBoolean(propName, mContext.getResources().getBoolean(resId));
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index b44f5f7..b141454 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.os.Handler;
 import android.util.Log;
+import android.view.View;
 import android.view.animation.Interpolator;
 
 import com.android.systemui.Interpolators;
@@ -40,6 +41,8 @@
     private final Handler mHandler = new Handler();
     private final ScrimController mScrimController;
 
+    private final View mStackScroller;
+
     private boolean mDozing;
     private DozeHost.PulseCallback mPulseCallback;
     private int mPulseReason;
@@ -48,7 +51,9 @@
     private float mInFrontTarget;
     private float mBehindTarget;
 
-    public DozeScrimController(ScrimController scrimController, Context context) {
+    public DozeScrimController(ScrimController scrimController, Context context,
+            View stackScroller) {
+        mStackScroller = stackScroller;
         mScrimController = scrimController;
         mDozeParameters = new DozeParameters(context);
     }
@@ -59,7 +64,11 @@
         if (mDozing) {
             abortAnimations();
             mScrimController.setDozeBehindAlpha(1f);
-            mScrimController.setDozeInFrontAlpha(1f);
+            mScrimController.setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? 0f : 1f);
+            if (mDozeParameters.getAlwaysOn()) {
+                mStackScroller.setAlpha(0f);
+                mHandler.postDelayed(() -> mStackScroller.setAlpha(0f), 30);
+            }
         } else {
             cancelPulsing();
             if (animate) {
@@ -74,6 +83,9 @@
                 mScrimController.setDozeBehindAlpha(0f);
                 mScrimController.setDozeInFrontAlpha(0f);
             }
+            if (mDozeParameters.getAlwaysOn()) {
+                mStackScroller.setAlpha(1f);
+            }
         }
     }
 
@@ -93,7 +105,9 @@
         // be invoked when we're done so that the caller can drop the pulse wakelock.
         mPulseCallback = callback;
         mPulseReason = reason;
-        mHandler.post(mPulseIn);
+        if (mDozeParameters.getAlwaysOn()) {
+            mHandler.post(mPulseIn);
+        }
     }
 
     /**
@@ -111,6 +125,9 @@
         if (isPulsing()) {
             final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
                     || mPulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
+            if (mDozeParameters.getAlwaysOn()) {
+                mStackScroller.setAlpha(1f);
+            }
             startScrimAnimation(true /* inFront */, 0f,
                     mDozeParameters.getPulseInDuration(pickupOrDoubleTap),
                     pickupOrDoubleTap ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT,
@@ -245,6 +262,10 @@
 
             // Signal that the pulse is ready to turn the screen on and draw.
             pulseStarted();
+
+            if (mDozeParameters.getAlwaysOn()) {
+                mHandler.post(DozeScrimController.this::onScreenTurnedOn);
+            }
         }
     };
 
@@ -262,7 +283,8 @@
         public void run() {
             if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
             if (!mDozing) return;
-            startScrimAnimation(true /* inFront */, 1f, mDozeParameters.getPulseOutDuration(),
+            startScrimAnimation(true /* inFront */, mDozeParameters.getAlwaysOn() ? 0 : 1,
+                    mDozeParameters.getPulseOutDuration(),
                     Interpolators.ALPHA_IN, mPulseOutFinished);
         }
     };
@@ -271,6 +293,9 @@
         @Override
         public void run() {
             if (DEBUG) Log.d(TAG, "Pulse out finished");
+            if (mDozeParameters.getAlwaysOn()) {
+                mStackScroller.setAlpha(0f);
+            }
             DozeLog.tracePulseFinish();
 
             // Signal that the pulse is all finished so we can turn the screen off now.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 28e5f7e..0ef2687 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -123,6 +123,7 @@
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.keyguard.KeyguardHostView.OnDismissAction;
+import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.ViewMediatorCallback;
@@ -367,7 +368,7 @@
     // top bar
     BaseStatusBarHeader mHeader;
     protected KeyguardStatusBarView mKeyguardStatusBar;
-    View mKeyguardStatusView;
+    KeyguardStatusView mKeyguardStatusView;
     KeyguardBottomAreaView mKeyguardBottomArea;
     boolean mLeaveOpenOnKeyguardHide;
     KeyguardIndicationController mKeyguardIndicationController;
@@ -846,10 +847,11 @@
         mHeadsUpManager.addListener(mScrimController);
         mStackScroller.setScrimController(mScrimController);
         mStatusBarView.setScrimController(mScrimController);
-        mDozeScrimController = new DozeScrimController(mScrimController, context);
+        mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller);
 
         mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
-        mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
+        mKeyguardStatusView =
+                (KeyguardStatusView) mStatusBarWindow.findViewById(R.id.keyguard_status_view);
         mKeyguardBottomArea =
                 (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
         mKeyguardBottomArea.setActivityStarter(this);
@@ -2695,6 +2697,10 @@
         mFalsingManager.onScreenOff();
     }
 
+    public boolean isPulsing() {
+        return mDozeScrimController.isPulsing();
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
@@ -5135,6 +5141,11 @@
         }
 
         @Override
+        public void dozeTimeTick() {
+            mKeyguardStatusView.refreshTime();
+        }
+
+        @Override
         public boolean isPowerSaveActive() {
             return mBatteryController != null && mBatteryController.isPowerSave();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 4425c5e..5696123 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -248,6 +248,10 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (mService.isDozing() && !mService.isPulsing()) {
+            // Discard all touch events in always-on.
+            return true;
+        }
         boolean intercept = false;
         if (mNotificationPanel.isFullyExpanded()
                 && mStackScrollLayout.getVisibility() == View.VISIBLE
@@ -274,6 +278,11 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
+        if (mService.isDozing() && !mService.isPulsing()) {
+            // Discard all touch events in always-on.
+            return true;
+        }
+
         boolean handled = false;
         if (mService.getBarState() == StatusBarState.KEYGUARD) {
             handled = mDragDownHelper.onTouchEvent(ev);