Fixing tiles on lockscreen and scrims transitions with QS expanded

This is quick and safe fix for most bottom tiles not working on lockscreen. We’re updating QS state based on shade transition from KEYGUARD to SHADE_LOCKED. For long term we’d likely prefer to not switch expansion state manually but rather for it to be changed automatically by increasing QS height as it’s done in UNLOCKED state.

Making QS state up-to-date changes scrim transitions as they depend on QS expansion. That caused b/230340979 in the past where scrim is dark for too long when transitioning between shade locked and bouncer. It was mostly caused by qs expansion being interpolated before calculated tint transition - all start and end values are still correct.

The easiest and low risk fix is to adjust scrim transition interpolation and that results in:
- Landscape: it doesn’t keep transition the same, but I think it still looks pretty good and - as a bonus - prevents issue of QS footer background being visible (b/232408997)
- Portrait: Two cases here on lockscreen: going directly to expanded QS or expanding shade and then expanding QS. Both of them didn’t look great to start with but this change makes it a bit better.

This change doesn’t influence handheld transitions as there start and end state tints are the same.

Bug: 228198572
Bug: 232365192
Test: manual
Change-Id: If5c00316bad5ba17a5a359b7ab8bf970ee709121
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 2dbbb145..1e34006 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -4707,6 +4707,11 @@
                     duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
                 }
                 mKeyguardStatusBarViewController.animateKeyguardStatusBarOut(startDelay, duration);
+                if (mSplitShadeEnabled) {
+                    // temporary workaround for QS height not being updated during lockscreen to
+                    // shade transition
+                    setQsExpanded(true);
+                }
                 updateQSMinHeight();
             } else if (oldState == StatusBarState.SHADE_LOCKED
                     && statusBarState == KEYGUARD) {
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 0e77866..5e7dc11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -906,9 +906,19 @@
         }
         if (mQsExpansion > 0) {
             behindAlpha = MathUtils.lerp(behindAlpha, mDefaultScrimAlpha, mQsExpansion);
+            float tintProgress = mQsExpansion;
+            if (mStatusBarKeyguardViewManager.isBouncerInTransit()) {
+                // this is case of - on lockscreen - going from expanded QS to bouncer.
+                // Because mQsExpansion is already interpolated and transition between tints
+                // is too slow, we want to speed it up and make it more aligned to bouncer
+                // showing up progress. This issue is visible on large screens, both portrait and
+                // split shade because then transition is between very different tints
+                tintProgress = BouncerPanelExpansionCalculator
+                        .showBouncerProgress(mPanelExpansionFraction);
+            }
             int stateTint = mClipsQsScrim ? ScrimState.SHADE_LOCKED.getNotifTint()
                     : ScrimState.SHADE_LOCKED.getBehindTint();
-            behindTint = ColorUtils.blendARGB(behindTint, stateTint, mQsExpansion);
+            behindTint = ColorUtils.blendARGB(behindTint, stateTint, tintProgress);
         }
 
         // If the keyguard is going away, we should not be opaque.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 5f8dda3..09009c6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -19,6 +19,8 @@
 import static com.android.systemui.statusbar.phone.ScrimController.OPAQUE;
 import static com.android.systemui.statusbar.phone.ScrimController.SEMI_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.ScrimController.TRANSPARENT;
+import static com.android.systemui.statusbar.phone.ScrimState.BOUNCER;
+import static com.android.systemui.statusbar.phone.ScrimState.SHADE_LOCKED;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -275,7 +277,7 @@
 
     @Test
     public void transitionToShadeLocked() {
-        mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+        mScrimController.transitionTo(SHADE_LOCKED);
         mScrimController.setQsPosition(1f, 0);
         finishAnimationsImmediately();
 
@@ -293,7 +295,7 @@
     @Test
     public void transitionToShadeLocked_clippingQs() {
         mScrimController.setClipsQsScrim(true);
-        mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+        mScrimController.transitionTo(SHADE_LOCKED);
         mScrimController.setQsPosition(1f, 0);
         finishAnimationsImmediately();
 
@@ -542,7 +544,7 @@
 
     @Test
     public void transitionToKeyguardBouncer() {
-        mScrimController.transitionTo(ScrimState.BOUNCER);
+        mScrimController.transitionTo(BOUNCER);
         finishAnimationsImmediately();
         // Front scrim should be transparent
         // Back scrim should be visible without tint
@@ -561,7 +563,7 @@
     @Test
     public void transitionToKeyguardBouncer_clippingQs() {
         mScrimController.setClipsQsScrim(true);
-        mScrimController.transitionTo(ScrimState.BOUNCER);
+        mScrimController.transitionTo(BOUNCER);
         finishAnimationsImmediately();
         // Front scrim should be transparent
         // Back scrim should be clipping QS
@@ -581,7 +583,7 @@
     @Test
     public void disableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
         mScrimController.setClipsQsScrim(true);
-        mScrimController.transitionTo(ScrimState.BOUNCER);
+        mScrimController.transitionTo(BOUNCER);
 
         mScrimController.setClipsQsScrim(false);
 
@@ -602,7 +604,7 @@
     @Test
     public void enableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
         mScrimController.setClipsQsScrim(false);
-        mScrimController.transitionTo(ScrimState.BOUNCER);
+        mScrimController.transitionTo(BOUNCER);
 
         mScrimController.setClipsQsScrim(true);
 
@@ -672,9 +674,9 @@
         finishAnimationsImmediately();
         assertEquals(mScrimState, ScrimState.UNLOCKED);
 
-        mScrimController.transitionTo(ScrimState.BOUNCER);
+        mScrimController.transitionTo(BOUNCER);
         finishAnimationsImmediately();
-        assertEquals(mScrimState, ScrimState.BOUNCER);
+        assertEquals(mScrimState, BOUNCER);
 
         mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
         finishAnimationsImmediately();
@@ -1081,9 +1083,9 @@
         HashSet<ScrimState> lowPowerModeStates = new HashSet<>(Arrays.asList(
                 ScrimState.OFF, ScrimState.AOD, ScrimState.PULSING));
         HashSet<ScrimState> regularStates = new HashSet<>(Arrays.asList(
-                ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, ScrimState.BOUNCER,
+                ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, BOUNCER,
                 ScrimState.DREAMING, ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR,
-                ScrimState.UNLOCKED, ScrimState.SHADE_LOCKED, ScrimState.AUTH_SCRIMMED,
+                ScrimState.UNLOCKED, SHADE_LOCKED, ScrimState.AUTH_SCRIMMED,
                 ScrimState.AUTH_SCRIMMED_SHADE));
 
         for (ScrimState state : ScrimState.values()) {
@@ -1228,7 +1230,7 @@
     @Test
     public void testNotificationScrimVisible_afterOpeningShadeFromLockscreen() {
         mScrimController.setRawPanelExpansionFraction(1);
-        mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+        mScrimController.transitionTo(SHADE_LOCKED);
         finishAnimationsImmediately();
 
         assertScrimAlpha(Map.of(
@@ -1237,10 +1239,26 @@
     }
 
     @Test
+    public void qsExpansion_BehindTint_shadeLocked_bouncerActive_usesBouncerProgress() {
+        when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true);
+        // clipping doesn't change tested logic but allows to assert scrims more in line with
+        // their expected large screen behaviour
+        mScrimController.setClipsQsScrim(false);
+        mScrimController.transitionTo(SHADE_LOCKED);
+
+        mScrimController.setQsPosition(1f, 100 /* value doesn't matter */);
+        assertTintAfterExpansion(mScrimBehind, SHADE_LOCKED.getBehindTint(), /* expansion= */ 1f);
+
+        mScrimController.setQsPosition(0.8f, 100 /* value doesn't matter */);
+        // panel expansion of 0.6 means its fully transitioned with bouncer progress interpolation
+        assertTintAfterExpansion(mScrimBehind, BOUNCER.getBehindTint(), /* expansion= */ 0.6f);
+    }
+
+    @Test
     public void expansionNotificationAlpha_shadeLocked_bouncerActive_usesBouncerInterpolator() {
         when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true);
 
-        mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+        mScrimController.transitionTo(SHADE_LOCKED);
 
         float expansion = 0.8f;
         float expectedAlpha =
@@ -1256,7 +1274,7 @@
     public void expansionNotificationAlpha_shadeLocked_bouncerNotActive_usesShadeInterpolator() {
         when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(false);
 
-        mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+        mScrimController.transitionTo(SHADE_LOCKED);
 
         float expansion = 0.8f;
         float expectedAlpha = ShadeInterpolation.getNotificationScrimAlpha(expansion);
@@ -1352,7 +1370,7 @@
 
     @Test
     public void testNotificationTransparency_followsTransitionToFullShade() {
-        mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+        mScrimController.transitionTo(SHADE_LOCKED);
         mScrimController.setRawPanelExpansionFraction(1.0f);
         finishAnimationsImmediately();
         float shadeLockedAlpha = mNotificationsScrim.getViewAlpha();
@@ -1379,7 +1397,7 @@
 
     @Test
     public void notificationTransparency_followsNotificationScrimProgress() {
-        mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+        mScrimController.transitionTo(SHADE_LOCKED);
         mScrimController.setRawPanelExpansionFraction(1.0f);
         finishAnimationsImmediately();
         mScrimController.transitionTo(ScrimState.KEYGUARD);
@@ -1473,7 +1491,7 @@
                 mScrimBehind, TRANSPARENT,
                 mNotificationsScrim, TRANSPARENT));
 
-        mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+        mScrimController.transitionTo(SHADE_LOCKED);
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
@@ -1504,6 +1522,15 @@
         assertEquals(expectedAlpha, scrim.getViewAlpha(), 0.2);
     }
 
+    private void assertTintAfterExpansion(ScrimView scrim, int expectedTint, float expansion) {
+        String message = "Tint test failed with expected scrim tint: "
+                + Integer.toHexString(expectedTint) + " and actual tint: "
+                + Integer.toHexString(scrim.getTint()) + " for scrim: " + getScrimName(scrim);
+        mScrimController.setRawPanelExpansionFraction(expansion);
+        finishAnimationsImmediately();
+        assertEquals(message, expectedTint, scrim.getTint(), 0.1);
+    }
+
     private void assertScrimTinted(Map<ScrimView, Boolean> scrimToTint) {
         scrimToTint.forEach((scrim, hasTint) -> assertScrimTint(scrim, hasTint));
     }