Fix issue where surface was animated while wake and unlocking.
This resulted in the wallpaper being briefly visible during wake and unlock, as the
surface alpha/translation animated. This is not the desired behavior, since we are
waking up from black/AOD, we don't want the surface to animate. The light reveal
scrim will make it visible.
Fixes: 201659992
Test: manual/atest SystemUITests
Change-Id: Id72f3124ee6c2c5c0867ac3cea8c2b04daa208c8
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index a801647..69bcf2e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -24,6 +24,7 @@
import android.view.RemoteAnimationTarget
import android.view.SyncRtSurfaceTransactionApplier
import android.view.View
+import androidx.annotation.VisibleForTesting
import androidx.core.math.MathUtils
import com.android.internal.R
import com.android.keyguard.KeyguardClockSwitchController
@@ -33,6 +34,7 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
+import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
@@ -93,7 +95,8 @@
keyguardViewMediator: Lazy<KeyguardViewMediator>,
private val keyguardViewController: KeyguardViewController,
private val smartspaceTransitionController: SmartspaceTransitionController,
- private val featureFlags: FeatureFlags
+ private val featureFlags: FeatureFlags,
+ private val biometricUnlockController: BiometricUnlockController
) : KeyguardStateController.Callback {
/**
@@ -107,7 +110,8 @@
* If we're unlocking via biometrics, PIN entry, or from clicking a notification, a canned
* animation is started in [notifyStartKeyguardExitAnimation].
*/
- private var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
+ @VisibleForTesting
+ var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
private var surfaceBehindRemoteAnimationTarget: RemoteAnimationTarget? = null
private var surfaceBehindRemoteAnimationStartTime: Long = 0
@@ -134,7 +138,8 @@
* Animator that animates in the surface behind the keyguard. This is used to play a canned
* animation on the surface, if we're not doing a swipe gesture.
*/
- private val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f)
+ @VisibleForTesting
+ val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f)
/** Rounded corner radius to apply to the surface behind the keyguard. */
private var roundedCornerRadius = 0f
@@ -222,7 +227,18 @@
// to animate it in. Otherwise, the swipe touch events will continue animating it.
if (!requestedShowSurfaceBehindKeyguard) {
keyguardViewController.hide(startTime, 350)
- surfaceBehindEntryAnimator.start()
+
+ // If we're wake and unlocking, we don't want to animate the surface since we're going
+ // to do the light reveal scrim from the black AOD screen. Make it visible and end the
+ // remote aimation.
+ if (biometricUnlockController.isWakeAndUnlock) {
+ setSurfaceBehindAppearAmount(1f)
+ keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ } else {
+ // Otherwise, animate it in normally.
+ surfaceBehindEntryAnimator.start()
+ }
}
// Finish the keyguard remote animation if the dismiss amount has crossed the threshold.
@@ -266,7 +282,7 @@
* animations and swipe gestures to animate the surface's entry (and exit, if the swipe is
* cancelled).
*/
- private fun setSurfaceBehindAppearAmount(amount: Float) {
+ fun setSurfaceBehindAppearAmount(amount: Float) {
if (surfaceBehindRemoteAnimationTarget == null) {
return
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
new file mode 100644
index 0000000..f3043e9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -0,0 +1,134 @@
+package com.android.systemui.keyguard
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration
+import android.graphics.Point
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.view.SyncRtSurfaceTransactionApplier
+import android.view.ViewRootImpl
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardViewController
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
+import com.android.systemui.statusbar.phone.BiometricUnlockController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
+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
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
+ private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
+
+ @Mock
+ private lateinit var keyguardViewMediator: KeyguardViewMediator
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+ @Mock
+ private lateinit var keyguardViewController: KeyguardViewController
+ @Mock
+ private lateinit var smartspaceTransitionController: SmartspaceTransitionController
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var biometricUnlockController: BiometricUnlockController
+ @Mock
+ private lateinit var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier
+
+ private lateinit var remoteAnimationTarget: RemoteAnimationTarget
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ keyguardUnlockAnimationController = KeyguardUnlockAnimationController(
+ context, keyguardStateController, { keyguardViewMediator }, keyguardViewController,
+ smartspaceTransitionController, featureFlags, biometricUnlockController
+ )
+
+ `when`(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
+
+ // 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.
+ remoteAnimationTarget = RemoteAnimationTarget(
+ 0, 0, null, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
+ mock(WindowConfiguration::class.java), false, mock(SurfaceControl::class.java), Rect(),
+ mock(ActivityManager.RunningTaskInfo::class.java), false)
+
+ // Set the surface applier to our mock so that we can verify the arguments passed to it.
+ // This applier does not have any side effects within the unlock animation controller, so
+ // this is a reasonable way to test.
+ keyguardUnlockAnimationController.surfaceTransactionApplier = surfaceTransactionApplier
+ }
+
+ /**
+ * 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,
+ * revealing the app/launcher below. In this case, we want to make sure we are not animating the
+ * surface, or the user will see the wallpaper briefly as the app animates in.
+ */
+ @Test
+ fun noSurfaceAnimation_ifWakeAndUnlocking() {
+ `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
+
+ keyguardUnlockAnimationController.notifyStartKeyguardExitAnimation(
+ remoteAnimationTarget,
+ 0 /* startTime */,
+ false /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java)
+ verify(surfaceTransactionApplier, times(1)).scheduleApply(captor.capture())
+
+ val params = captor.value
+
+ // We expect that we've instantly set the surface behind to alpha = 1f, and have no
+ // transforms (translate, scale) on its matrix.
+ assertEquals(params.alpha, 1f)
+ assertTrue(params.matrix.isIdentity)
+
+ // Also expect we've immediately asked the keyguard view mediator to finish the remote
+ // animation.
+ verify(keyguardViewMediator, times(1)).onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+
+ verifyNoMoreInteractions(surfaceTransactionApplier)
+ }
+
+ /**
+ * If we are not wake and unlocking, we expect the unlock animation to play normally.
+ */
+ @Test
+ fun surfaceAnimation_ifNotWakeAndUnlocking() {
+ `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(false)
+
+ keyguardUnlockAnimationController.notifyStartKeyguardExitAnimation(
+ remoteAnimationTarget,
+ 0 /* startTime */,
+ false /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ // Make sure the animator was started.
+ assertTrue(keyguardUnlockAnimationController.surfaceBehindEntryAnimator.isRunning)
+
+ // Since the animation is running, we should not have finished the remote animation.
+ verify(keyguardViewMediator, times(0)).onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ }
+}
\ No newline at end of file