Hide AoD wallpaper after 1min

Hide wallpaper by cross fading back scrim. This increases
battery life since black pixels won't drain power.

Bug: 64155983
Test: atest packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
Test: set AoD wallpaper, wait for 1 min.
Test: set AoD wallpaper, wait for 50 sec, go to pulsing, wait 10 sec.
Change-Id: Ie830c2fd20f9e60efbfd9e78f248603df07ae93c
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 69809a2..6d85fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -26,6 +26,7 @@
 
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.systemui.R;
+import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 
 import java.io.PrintWriter;
 
@@ -37,10 +38,12 @@
     private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
 
     private static IntInOutMatcher sPickupSubtypePerformsProxMatcher;
+    private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
 
     public DozeParameters(Context context) {
         mContext = context;
         mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
+        mAlwaysOnPolicy = new AlwaysOnDisplayPolicy(context);
     }
 
     public void dump(PrintWriter pw) {
@@ -121,6 +124,22 @@
     }
 
     /**
+     * For how long a wallpaper can be visible in AoD before it fades aways.
+     * @return duration in millis.
+     */
+    public long getWallpaperAodDuration() {
+        return mAlwaysOnPolicy.wallpaperVisibilityDuration;
+    }
+
+    /**
+     * How long it takes for the wallpaper fade away (Animation duration.)
+     * @return duration in millis.
+     */
+    public long getWallpaperFadeOutDuration() {
+        return mAlwaysOnPolicy.wallpaperFadeOutDuration;
+    }
+
+    /**
      * Checks if always on is available and enabled for the current user.
      * @return {@code true} if enabled and available.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index a809c9e..8abc3f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -20,6 +20,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
+import android.app.AlarmManager;
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.graphics.Color;
@@ -51,6 +52,7 @@
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.util.AlarmTimeout;
 import com.android.systemui.util.wakelock.DelayedWakeLock;
 import com.android.systemui.util.wakelock.WakeLock;
 
@@ -73,6 +75,19 @@
             = new PathInterpolator(0f, 0, 0.7f, 1f);
     public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED
             = new PathInterpolator(0.3f, 0f, 0.8f, 1f);
+
+    /**
+     * When both scrims have 0 alpha.
+     */
+    public static final int VISIBILITY_FULLY_TRANSPARENT = 0;
+    /**
+     * When scrims aren't transparent (alpha 0) but also not opaque (alpha 1.)
+     */
+    public static final int VISIBILITY_SEMI_TRANSPARENT = 1;
+    /**
+     * When at least 1 scrim is fully opaque (alpha set to 1.)
+     */
+    public static final int VISIBILITY_FULLY_OPAQUE = 2;
     /**
      * Default alpha value for most scrims.
      */
@@ -111,6 +126,7 @@
     private final UnlockMethodCache mUnlockMethodCache;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final DozeParameters mDozeParameters;
+    private final AlarmTimeout mTimeTicker;
 
     private final SysuiColorExtractor mColorExtractor;
     private GradientColors mLockColors;
@@ -138,23 +154,25 @@
     private float mCurrentBehindAlpha = NOT_INITIALIZED;
     private int mCurrentInFrontTint;
     private int mCurrentBehindTint;
+    private boolean mWallpaperVisibilityTimedOut;
     private int mPinnedHeadsUpCount;
     private float mTopHeadsUpDragAmount;
     private View mDraggedHeadsUpView;
     private boolean mKeyguardFadingOutInProgress;
     private ValueAnimator mKeyguardFadeoutAnimation;
-    private boolean mScrimsVisible;
-    private final Consumer<Boolean> mScrimVisibleListener;
+    private int mScrimsVisibility;
+    private final Consumer<Integer> mScrimVisibleListener;
     private boolean mBlankScreen;
     private boolean mScreenBlankingCallbackCalled;
     private Callback mCallback;
+    private boolean mWallpaperSupportsAmbientMode;
 
     private final WakeLock mWakeLock;
     private boolean mWakeLockHeld;
 
     public ScrimController(LightBarController lightBarController, ScrimView scrimBehind,
-            ScrimView scrimInFront, View headsUpScrim, Consumer<Boolean> scrimVisibleListener,
-            DozeParameters dozeParameters) {
+            ScrimView scrimInFront, View headsUpScrim, Consumer<Integer> scrimVisibleListener,
+            DozeParameters dozeParameters, AlarmManager alarmManager) {
         mScrimBehind = scrimBehind;
         mScrimInFront = scrimInFront;
         mHeadsUpScrim = headsUpScrim;
@@ -164,6 +182,8 @@
         mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
         mLightBarController = lightBarController;
         mScrimBehindAlphaResValue = mContext.getResources().getFloat(R.dimen.scrim_behind_alpha);
+        mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout,
+                "hide_aod_wallpaper", new Handler());
         mWakeLock = createWakeLock();
         // Scrim alpha is initially set to the value on the resource but might be changed
         // to make sure that text on top of it is legible.
@@ -236,13 +256,18 @@
         }
 
         // Do not let the device sleep until we're done with all animations
-        if (!mWakeLockHeld) {
-            if (mWakeLock != null) {
-                mWakeLockHeld = true;
-                mWakeLock.acquire();
-            } else {
-                Log.w(TAG, "Cannot hold wake lock, it has not been set yet");
+        holdWakeLock();
+
+        // AOD wallpapers should fade away after a while
+        if (mWallpaperSupportsAmbientMode && mDozeParameters.getAlwaysOn()
+                && (mState == ScrimState.AOD || mState == ScrimState.PULSING)) {
+            if (!mWallpaperVisibilityTimedOut) {
+                mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
+                        AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
             }
+        } else {
+            mTimeTicker.cancel();
+            mWallpaperVisibilityTimedOut = false;
         }
 
         if (!mKeyguardUpdateMonitor.needsSlowUnlockTransition()) {
@@ -279,6 +304,30 @@
         mTracking = false;
     }
 
+    @VisibleForTesting
+    protected void onHideWallpaperTimeout() {
+        if (mState != ScrimState.AOD && mState != ScrimState.PULSING) {
+            return;
+        }
+
+        holdWakeLock();
+        mWallpaperVisibilityTimedOut = true;
+        mAnimateChange = true;
+        mAnimationDuration = mDozeParameters.getWallpaperFadeOutDuration();
+        scheduleUpdate();
+    }
+
+    private void holdWakeLock() {
+        if (!mWakeLockHeld) {
+            if (mWakeLock != null) {
+                mWakeLockHeld = true;
+                mWakeLock.acquire();
+            } else {
+                Log.w(TAG, "Cannot hold wake lock, it has not been set yet");
+            }
+        }
+    }
+
     /**
      * Current state of the shade expansion when pulling it from the top.
      * This value is 1 when on top of the keyguard and goes to 0 as the user drags up.
@@ -391,6 +440,14 @@
             mLightBarController.setScrimColor(mScrimInFront.getColors());
         }
 
+        // We want to override the back scrim opacity for AOD and PULSING
+        // when it's time to fade the wallpaper away.
+        boolean overrideBackScrimAlpha = (mState == ScrimState.PULSING || mState == ScrimState.AOD)
+                && mWallpaperVisibilityTimedOut;
+        if (overrideBackScrimAlpha) {
+            mCurrentBehindAlpha = 1;
+        }
+
         setScrimInFrontAlpha(mCurrentInFrontAlpha);
         setScrimBehindAlpha(mCurrentBehindAlpha);
 
@@ -398,12 +455,18 @@
     }
 
     private void dispatchScrimsVisible() {
-        boolean scrimsVisible = mScrimBehind.getViewAlpha() > 0 || mScrimInFront.getViewAlpha() > 0;
+        final int currentScrimVisibility;
+        if (mScrimInFront.getViewAlpha() == 1 || mScrimBehind.getViewAlpha() == 1) {
+            currentScrimVisibility = VISIBILITY_FULLY_OPAQUE;
+        } else if (mScrimInFront.getViewAlpha() == 0 && mScrimBehind.getViewAlpha() == 0) {
+            currentScrimVisibility = VISIBILITY_FULLY_TRANSPARENT;
+        } else {
+            currentScrimVisibility = VISIBILITY_SEMI_TRANSPARENT;
+        }
 
-        if (mScrimsVisible != scrimsVisible) {
-            mScrimsVisible = scrimsVisible;
-
-            mScrimVisibleListener.accept(scrimsVisible);
+        if (mScrimsVisibility != currentScrimVisibility) {
+            mScrimsVisibility = currentScrimVisibility;
+            mScrimVisibleListener.accept(currentScrimVisibility);
         }
     }
 
@@ -812,7 +875,11 @@
     }
 
     public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
-        ScrimState.AOD.setWallpaperSupportsAmbientMode(wallpaperSupportsAmbientMode);
+        mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
+        ScrimState[] states = ScrimState.values();
+        for (int i = 0; i < states.length; i++) {
+            states[i].setWallpaperSupportsAmbientMode(wallpaperSupportsAmbientMode);
+        }
     }
 
     public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index f6b5e6e..fa2c1b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -19,9 +19,7 @@
 import android.graphics.Color;
 import android.os.Trace;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
@@ -87,19 +85,13 @@
      */
     AOD {
         @Override
-        public void init(ScrimView scrimInFront, ScrimView scrimBehind,
-                DozeParameters dozeParameters) {
-            super.init(scrimInFront, scrimBehind, dozeParameters);
-            mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(scrimInFront.getContext());
-        }
-
-        @Override
         public void prepare(ScrimState previousState) {
             if (previousState == ScrimState.PULSING && !mCanControlScreenOff) {
                 updateScrimColor(mScrimInFront, 1, Color.BLACK);
             }
             final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
-            mBlankScreen = previousState == ScrimState.PULSING;
+            final boolean wasPulsing = previousState == ScrimState.PULSING;
+            mBlankScreen = wasPulsing && !mCanControlScreenOff;
             mCurrentBehindAlpha = mWallpaperSupportsAmbientMode
                     && !mKeyguardUpdateMonitor.hasLockscreenWallpaper() ? 0f : 1f;
             mCurrentInFrontAlpha = alwaysOnEnabled ? mAodFrontScrimAlpha : 1f;
@@ -117,9 +109,10 @@
     PULSING {
         @Override
         public void prepare(ScrimState previousState) {
-            mCurrentBehindAlpha = 1;
             mCurrentInFrontAlpha = 0;
             mCurrentInFrontTint = Color.BLACK;
+            mCurrentBehindAlpha = mWallpaperSupportsAmbientMode
+                    && !mKeyguardUpdateMonitor.hasLockscreenWallpaper() ? 0f : 1f;
             mCurrentBehindTint = Color.BLACK;
             mBlankScreen = mDisplayRequiresBlanking;
             if (mDisplayRequiresBlanking) {
@@ -178,6 +171,7 @@
         mDozeParameters = dozeParameters;
         mDisplayRequiresBlanking = dozeParameters.getDisplayNeedsBlanking();
         mCanControlScreenOff = dozeParameters.getCanControlScreenOffAnimation();
+        mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(scrimInFront.getContext());
     }
 
     public void prepare(ScrimState previousState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 87815a3..c5349d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -46,6 +46,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
+import android.app.AlarmManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -250,7 +251,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Stack;
-import java.util.function.Function;
 
 public class StatusBar extends SystemUI implements DemoMode,
         DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
@@ -943,9 +943,9 @@
                 scrimBehind, scrimInFront, headsUpScrim, mLockscreenWallpaper,
                 scrimsVisible -> {
                     if (mStatusBarWindowManager != null) {
-                        mStatusBarWindowManager.setScrimsVisible(scrimsVisible);
+                        mStatusBarWindowManager.setScrimsVisibility(scrimsVisible);
                     }
-                }, new DozeParameters(mContext));
+                }, new DozeParameters(mContext), mContext.getSystemService(AlarmManager.class));
         if (mScrimSrcModeEnabled) {
             Runnable runnable = () -> {
                 boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 47be4d0..c30f633 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -138,7 +138,8 @@
         }
 
         final boolean showWallpaperOnAod = mDozeParameters.getAlwaysOn() &&
-                state.wallpaperSupportsAmbientMode;
+                state.wallpaperSupportsAmbientMode &&
+                state.scrimsVisibility != ScrimController.VISIBILITY_FULLY_OPAQUE;
         if (state.keyguardShowing && !state.backdropShowing &&
                 (!state.dozing || showWallpaperOnAod)) {
             mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -192,7 +193,8 @@
     private boolean isExpanded(State state) {
         return !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
                 || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
-                || state.headsUpShowing || state.scrimsVisible);
+                || state.headsUpShowing
+                || state.scrimsVisibility != ScrimController.VISIBILITY_FULLY_TRANSPARENT);
     }
 
     private void applyFitsSystemWindows(State state) {
@@ -340,8 +342,8 @@
         apply(mCurrentState);
     }
 
-    public void setScrimsVisible(boolean scrimsVisible) {
-        mCurrentState.scrimsVisible = scrimsVisible;
+    public void setScrimsVisibility(int scrimsVisibility) {
+        mCurrentState.scrimsVisibility = scrimsVisibility;
         apply(mCurrentState);
     }
 
@@ -452,7 +454,7 @@
         boolean remoteInputActive;
         boolean forcePluginOpen;
         boolean dozing;
-        boolean scrimsVisible;
+        int scrimsVisibility;
 
         private boolean isKeyguardShowingAndNotOccluded() {
             return keyguardShowing && !keyguardOccluded;