diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index 333815f6b11aa..0280e0b729a9e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -44,6 +44,7 @@ import android.view.animation.Interpolator; import androidx.annotation.NonNull; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.jank.InteractionJankMonitor.Configuration; import com.android.internal.logging.UiEventLogger; @@ -335,10 +336,24 @@ public class StatusBarStateControllerImpl implements ? Interpolators.FAST_OUT_SLOW_IN : Interpolators.TOUCH_RESPONSE_REVERSE; } - mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, mDozeAmountTarget); - mDarkAnimator.setInterpolator(Interpolators.LINEAR); - mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP); - mDarkAnimator.addListener(new AnimatorListenerAdapter() { + if (mDozeAmount == 1f && !mIsDozing) { + // Workaround to force relayoutWindow to be called a frame earlier. Otherwise, if + // mDozeAmount = 1f, then neither start() nor the first frame of the animation will + // cause the scrim opacity to change, which ultimately results in an extra relayout and + // causes us to miss a frame. By settings the doze amount to be <1f a frame earlier, + // we can batch the relayout with the one in NotificationShadeWindowControllerImpl. + setDozeAmountInternal(0.99f); + } + mDarkAnimator = createDarkAnimator(); + } + + @VisibleForTesting + protected ObjectAnimator createDarkAnimator() { + ObjectAnimator darkAnimator = ObjectAnimator.ofFloat( + this, SET_DARK_AMOUNT_PROPERTY, mDozeAmountTarget); + darkAnimator.setInterpolator(Interpolators.LINEAR); + darkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP); + darkAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationCancel(Animator animation) { cancelInteractionJankMonitor(); @@ -354,7 +369,8 @@ public class StatusBarStateControllerImpl implements beginInteractionJankMonitor(); } }); - mDarkAnimator.start(); + darkAnimator.start(); + return darkAnimator; } private void setDozeAmountInternal(float dozeAmount) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt index bc1abe62c7ffe..be631afdd1a98 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar +import android.animation.ObjectAnimator import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest @@ -46,6 +47,7 @@ import org.mockito.Mockito.`when` as whenever class StatusBarStateControllerImplTest : SysuiTestCase() { @Mock lateinit var interactionJankMonitor: InteractionJankMonitor + @Mock private lateinit var mockDarkAnimator: ObjectAnimator private lateinit var controller: StatusBarStateControllerImpl private lateinit var uiEventLogger: UiEventLoggerFake @@ -57,11 +59,13 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { whenever(interactionJankMonitor.end(anyInt())).thenReturn(true) uiEventLogger = UiEventLoggerFake() - controller = StatusBarStateControllerImpl( + controller = object : StatusBarStateControllerImpl( uiEventLogger, mock(DumpManager::class.java), interactionJankMonitor - ) + ) { + override fun createDarkAnimator(): ObjectAnimator { return mockDarkAnimator } + } } @Test @@ -127,4 +131,23 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { // Double check that we can still force it to happen. assertTrue(controller.setState(StatusBarState.SHADE, true /* force */)) } + + @Test + fun testSetDozeAmount_immediatelyChangesDozeAmount_lockscreenTransitionFromAod() { + // Put controller in AOD state + controller.setDozeAmount(1f, false) + + // When waking from doze, CentralSurfaces#updateDozingState will update the dozing state + // before the doze amount changes + controller.setIsDozing(false) + + // Animate the doze amount to 0f, as would normally happen + controller.setAndInstrumentDozeAmount(null, 0f, true) + + // Check that the doze amount is immediately set to a value slightly less than 1f. This is + // to ensure that any scrim implementation changes its opacity immediately rather than + // waiting an extra frame. Waiting an extra frame will cause a relayout (which is expensive) + // and cause us to drop a frame during the LOCKSCREEN_TRANSITION_FROM_AOD CUJ. + assertEquals(0.99f, controller.dozeAmount, 0.009f) + } }