From 767467732e3c237293a1c2734e7feb1040f66070 Mon Sep 17 00:00:00 2001 From: Josh Tsuji Date: Thu, 22 Dec 2022 16:11:54 -0500 Subject: [PATCH] Override alpha to 0f if we're no longer interactive. This is a safe way to ensure the surface is immediately no longer visible, until the screen off lifecycle event arrives and ends up canceling the unlock sequence and sleeping the device. WM resets the surface alpha after the animation ends, so this should be safe. Since the surface alpha is usually close to 0f when this condition is met, it's not usually perceptible that we jump-cut to 0f. Test: press the power button while also triggering an unlock Test: atest KeyguardUnlockAnimationControllerTest Bug: 260094933 Change-Id: I15a9decf1dcfac0b60b3f4e97a2e470098961743 --- .../KeyguardUnlockAnimationController.kt | 17 +++-- .../KeyguardUnlockAnimationControllerTest.kt | 73 +++++++++++++++++-- 2 files changed, 77 insertions(+), 13 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 53070a0ab8a7a..7f942b85b8200 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -23,6 +23,7 @@ import android.content.Context import android.graphics.Matrix import android.graphics.Rect import android.os.Handler +import android.os.PowerManager import android.os.RemoteException import android.util.Log import android.view.RemoteAnimationTarget @@ -145,7 +146,8 @@ class KeyguardUnlockAnimationController @Inject constructor( private val featureFlags: FeatureFlags, private val biometricUnlockControllerLazy: Lazy, private val statusBarStateController: SysuiStatusBarStateController, - private val notificationShadeWindowController: NotificationShadeWindowController + private val notificationShadeWindowController: NotificationShadeWindowController, + private val powerManager: PowerManager ) : KeyguardStateController.Callback, ISysuiUnlockAnimationController.Stub() { interface KeyguardUnlockAnimationListener { @@ -783,10 +785,15 @@ class KeyguardUnlockAnimationController @Inject constructor( surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y ) - // If we're snapping the keyguard back, immediately begin fading it out. - val animationAlpha = - if (keyguardStateController.isSnappingKeyguardBackAfterSwipe) amount - else surfaceBehindAlpha + + val animationAlpha = when { + // If we're snapping the keyguard back, immediately begin fading it out. + keyguardStateController.isSnappingKeyguardBackAfterSwipe -> amount + // If the screen has turned back off, the unlock animation is going to be cancelled, + // so set the surface alpha to 0f so it's no longer visible. + !powerManager.isInteractive -> 0f + else -> surfaceBehindAlpha + } // SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is // unable to draw diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt index a78c902a1f305..77880101e3b98 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt @@ -4,6 +4,7 @@ import android.app.ActivityManager import android.app.WindowConfiguration import android.graphics.Point import android.graphics.Rect +import android.os.PowerManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.RemoteAnimationTarget @@ -19,15 +20,16 @@ import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.mockito.whenever import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor.forClass import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.mock import org.mockito.Mockito.times import org.mockito.Mockito.verify @@ -56,6 +58,8 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { private lateinit var statusBarStateController: SysuiStatusBarStateController @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController + @Mock + private lateinit var powerManager: PowerManager @Mock private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController.Stub @@ -79,12 +83,13 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController = KeyguardUnlockAnimationController( context, keyguardStateController, { keyguardViewMediator }, keyguardViewController, featureFlags, { biometricUnlockController }, statusBarStateController, - notificationShadeWindowController + notificationShadeWindowController, powerManager ) keyguardUnlockAnimationController.setLauncherUnlockController( launcherUnlockAnimationController) - `when`(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java)) + whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java)) + whenever(powerManager.isInteractive).thenReturn(true) // All of these fields are final, so we can't mock them, but are needed so that the surface // appear amount setter doesn't short circuit. @@ -96,6 +101,12 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.surfaceTransactionApplier = surfaceTransactionApplier } + @After + fun tearDown() { + keyguardUnlockAnimationController.surfaceBehindEntryAnimator.cancel() + keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.cancel() + } + /** * If we're wake and unlocking, we are animating from the black/AOD screen to the app/launcher * underneath. The LightRevealScrim will animate circularly from the fingerprint reader, @@ -104,7 +115,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { */ @Test fun noSurfaceAnimation_ifWakeAndUnlocking() { - `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true) + whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true) keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, @@ -135,7 +146,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { */ @Test fun surfaceAnimation_ifNotWakeAndUnlocking() { - `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(false) + whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(false) keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, @@ -159,7 +170,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { */ @Test fun fadeInSurfaceBehind_ifRequestedShowSurface_butNotFlinging() { - `when`(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(false) + whenever(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(false) keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, @@ -181,7 +192,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { */ @Test fun playCannedUnlockAnimation_ifRequestedShowSurface_andFlinging() { - `when`(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(true) + whenever(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(true) keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, @@ -215,7 +226,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { @Test fun doNotPlayCannedUnlockAnimation_ifLaunchingApp() { - `when`(notificationShadeWindowController.isLaunchingActivity).thenReturn(true) + whenever(notificationShadeWindowController.isLaunchingActivity).thenReturn(true) keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, @@ -275,4 +286,50 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { verify(keyguardViewMediator, times(0)).onKeyguardExitRemoteAnimationFinished( false /* cancelled */) } + + @Test + fun surfaceBehindAlphaOverriddenTo0_ifNotInteractive() { + whenever(powerManager.isInteractive).thenReturn(false) + + keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( + remoteAnimationTargets, + 0 /* startTime */, + false /* requestedShowSurfaceBehindKeyguard */ + ) + + keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f) + + val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java) + verify(surfaceTransactionApplier, times(1)).scheduleApply(captor.capture()) + + val params = captor.value + + // We expect that we've set the surface behind to alpha = 0f since we're not interactive. + assertEquals(params.alpha, 0f) + assertTrue(params.matrix.isIdentity) + + verifyNoMoreInteractions(surfaceTransactionApplier) + } + + @Test + fun surfaceBehindAlphaNotOverriddenTo0_ifInteractive() { + whenever(powerManager.isInteractive).thenReturn(true) + + keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( + remoteAnimationTargets, + 0 /* startTime */, + false /* requestedShowSurfaceBehindKeyguard */ + ) + + keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f) + + val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java) + verify(surfaceTransactionApplier, times(1)).scheduleApply(captor.capture()) + + val params = captor.value + assertEquals(params.alpha, 1f) + assertTrue(params.matrix.isIdentity) + + verifyNoMoreInteractions(surfaceTransactionApplier) + } }