diff options
3 files changed, 174 insertions, 19 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 2925d8dbd03a..872a32cfc24d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -245,6 +245,7 @@ class KeyguardUnlockAnimationController @Inject constructor( @VisibleForTesting var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null private var surfaceBehindRemoteAnimationTargets: Array<RemoteAnimationTarget>? = null + private var wallpaperTargets: Array<RemoteAnimationTarget>? = null private var surfaceBehindRemoteAnimationStartTime: Long = 0 /** @@ -257,9 +258,13 @@ class KeyguardUnlockAnimationController @Inject constructor( */ private var surfaceBehindAlpha = 1f + private var wallpaperAlpha = 1f + @VisibleForTesting var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f) + var wallpaperAlphaAnimator = ValueAnimator.ofFloat(0f, 1f) + /** * Matrix applied to [surfaceBehindRemoteAnimationTarget], which is the surface of the * app/launcher behind the keyguard. @@ -335,6 +340,27 @@ class KeyguardUnlockAnimationController @Inject constructor( }) } + with(wallpaperAlphaAnimator) { + duration = LAUNCHER_ICONS_ANIMATION_DURATION_MS + interpolator = Interpolators.ALPHA_OUT + addUpdateListener { valueAnimator: ValueAnimator -> + wallpaperAlpha = valueAnimator.animatedValue as Float + setWallpaperAppearAmount(wallpaperAlpha) + } + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + Log.d(TAG, "wallpaperAlphaAnimator#onAnimationEnd, animation ended ") + if (wallpaperAlpha == 1f) { + wallpaperTargets = null + keyguardViewMediator.get().finishExitRemoteAnimation() + } else { + Log.d(TAG, "wallpaperAlphaAnimator#onAnimationEnd, " + + "animation was cancelled: skipping finishAnimation()") + } + } + }) + } + with(surfaceBehindEntryAnimator) { duration = UNLOCK_ANIMATION_DURATION_MS startDelay = UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS @@ -361,6 +387,11 @@ class KeyguardUnlockAnimationController @Inject constructor( context.resources.getDimensionPixelSize(R.dimen.rounded_corner_radius).toFloat() } + fun isAnyKeyguyardAnimatorPlaying(): Boolean { + return surfaceBehindAlphaAnimator.isStarted || + wallpaperAlphaAnimator.isStarted || surfaceBehindEntryAnimator.isStarted + } + /** * Add a listener to be notified of various stages of the unlock animation. */ @@ -492,6 +523,7 @@ class KeyguardUnlockAnimationController @Inject constructor( */ fun notifyStartSurfaceBehindRemoteAnimation( targets: Array<RemoteAnimationTarget>, + wallpapers: Array<RemoteAnimationTarget>, startTime: Long, requestedShowSurfaceBehindKeyguard: Boolean ) { @@ -501,8 +533,11 @@ class KeyguardUnlockAnimationController @Inject constructor( } surfaceBehindRemoteAnimationTargets = targets + wallpaperTargets = wallpapers surfaceBehindRemoteAnimationStartTime = startTime + fadeInWallpaper() + // If we specifically requested that the surface behind be made visible (vs. it being made // visible because we're unlocking), then we're in the middle of a swipe-to-unlock touch // gesture and the surface behind the keyguard should be made visible so that we can animate @@ -839,6 +874,38 @@ class KeyguardUnlockAnimationController @Inject constructor( } /** + * Modify the opacity of a wallpaper window. + */ + fun setWallpaperAppearAmount(amount: Float) { + wallpaperTargets?.forEach { wallpaper -> + val animationAlpha = when { + // 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 -> amount + } + + // SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is + // unable to draw + val sc: SurfaceControl? = wallpaper.leash + if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE && + sc?.isValid == true) { + with(SurfaceControl.Transaction()) { + setAlpha(sc, animationAlpha) + apply() + } + } else { + applyParamsToSurface( + SyncRtSurfaceTransactionApplier.SurfaceParams.Builder( + wallpaper.leash) + .withAlpha(animationAlpha) + .build() + ) + } + } + } + + /** * Called by [KeyguardViewMediator] to let us know that the remote animation has finished, and * we should clean up all of our state. * @@ -903,6 +970,12 @@ class KeyguardUnlockAnimationController @Inject constructor( surfaceBehindAlphaAnimator.start() } + private fun fadeInWallpaper() { + Log.d(TAG, "fadeInWallpaper") + wallpaperAlphaAnimator.cancel() + wallpaperAlphaAnimator.start() + } + private fun fadeOutSurfaceBehind() { Log.d(TAG, "fadeOutSurfaceBehind") surfaceBehindAlphaAnimator.cancel() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 1a126d72f9e9..0cee65f77201 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -156,6 +156,7 @@ import dagger.Lazy; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.concurrent.Executor; /** @@ -2686,9 +2687,13 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "DismissPanel")); // Pass the surface and metadata to the unlock animation controller. + RemoteAnimationTarget[] openingWallpapers = Arrays.stream(wallpapers).filter( + w -> w.mode == RemoteAnimationTarget.MODE_OPENING).toArray( + RemoteAnimationTarget[]::new); mKeyguardUnlockAnimationControllerLazy.get() .notifyStartSurfaceBehindRemoteAnimation( - apps, startTime, mSurfaceBehindRemoteAnimationRequested); + apps, openingWallpapers, startTime, + mSurfaceBehindRemoteAnimationRequested); } else { mInteractionJankMonitor.begin( createInteractionJankMonitorConf( @@ -2937,6 +2942,19 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mSurfaceBehindRemoteAnimationRequested = false; mSurfaceBehindRemoteAnimationRunning = false; mKeyguardStateController.notifyKeyguardGoingAway(false); + finishExitRemoteAnimation(); + } + + void finishExitRemoteAnimation() { + if (mKeyguardUnlockAnimationControllerLazy.get().isAnyKeyguyardAnimatorPlaying() + || mKeyguardStateController.isDismissingFromSwipe()) { + // If the animation is ongoing, or we are not done with the swipe gesture, + // it's too early to terminate the animation + Log.d(TAG, "finishAnimation not executing now because " + + "not all animations have finished"); + return; + } + Log.d(TAG, "finishAnimation executing"); if (mSurfaceBehindRemoteAnimationFinishedCallback != null) { try { 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 2c81e82f34df..47df64fc33e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt @@ -20,6 +20,7 @@ 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.argThat import com.android.systemui.util.mockito.whenever import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse @@ -28,13 +29,14 @@ 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.atLeastOnce import org.mockito.Mockito.mock import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations +import java.util.function.Predicate @RunWith(AndroidTestingRunner::class) @RunWithLooper @@ -77,6 +79,13 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { mock(ActivityManager.RunningTaskInfo::class.java), false) private lateinit var remoteAnimationTargets: Array<RemoteAnimationTarget> + private var surfaceControlWp = mock(SurfaceControl::class.java) + private var wallpaperTarget = RemoteAnimationTarget( + 2 /* taskId */, 0, surfaceControlWp, false, Rect(), Rect(), 0, Point(), Rect(), Rect(), + mock(WindowConfiguration::class.java), false, surfaceControlWp, Rect(), + mock(ActivityManager.RunningTaskInfo::class.java), false) + private lateinit var wallpaperTargets: Array<RemoteAnimationTarget> + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -94,6 +103,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { // 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. remoteAnimationTargets = arrayOf(remoteTarget1) + wallpaperTargets = arrayOf(wallpaperTarget) // 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 @@ -119,18 +129,20 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, + arrayOf(), 0 /* startTime */, false /* requestedShowSurfaceBehindKeyguard */ ) - val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java) - verify(surfaceTransactionApplier, times(1)).scheduleApply(captor.capture()) + val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() + verify(surfaceTransactionApplier, times(1)).scheduleApply( + captorSb.capture { sp -> sp.surface == surfaceControl1 }) - val params = captor.value + val params = captorSb.getLastValue() // 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) + assertEquals(1f, params.alpha) assertTrue(params.matrix.isIdentity) // Also expect we've immediately asked the keyguard view mediator to finish the remote @@ -150,6 +162,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, + wallpaperTargets, 0 /* startTime */, false /* requestedShowSurfaceBehindKeyguard */ ) @@ -174,6 +187,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, + wallpaperTargets, 0 /* startTime */, true /* requestedShowSurfaceBehindKeyguard */ ) @@ -196,6 +210,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, + wallpaperTargets, 0 /* startTime */, true /* requestedShowSurfaceBehindKeyguard */ ) @@ -216,6 +231,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { fun playCannedUnlockAnimation_ifDidNotRequestShowSurface() { keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, + wallpaperTargets, 0 /* startTime */, false /* requestedShowSurfaceBehindKeyguard */ ) @@ -230,6 +246,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, + wallpaperTargets, 0 /* startTime */, true /* requestedShowSurfaceBehindKeyguard */ ) @@ -245,6 +262,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, + wallpaperTargets, 0 /* startTime */, false /* requestedShowSurfaceBehindKeyguard */ ) @@ -259,6 +277,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { fun surfaceAnimation_multipleTargets() { keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( arrayOf(remoteTarget1, remoteTarget2), + wallpaperTargets, 0 /* startTime */, false /* requestedShowSurfaceBehindKeyguard */ ) @@ -267,10 +286,15 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { // means an animation is in progress. keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(0.5f) - val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java) - verify(surfaceTransactionApplier, times(2)).scheduleApply(captor.capture()) + val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() + verify(surfaceTransactionApplier, times(2)).scheduleApply(captorSb + .capture { sp -> sp.surface == surfaceControl1 || sp.surface == surfaceControl2 }) + val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() + verify(surfaceTransactionApplier, times(1).description( + "WallpaperSurface was expected to receive scheduleApply once" + )).scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp}) - val allParams = captor.allValues + val allParams = captorSb.getAllValues() val remainingTargets = mutableListOf(surfaceControl1, surfaceControl2) allParams.forEach { params -> @@ -293,20 +317,29 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, + wallpaperTargets, 0 /* startTime */, false /* requestedShowSurfaceBehindKeyguard */ ) keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f) + keyguardUnlockAnimationController.setWallpaperAppearAmount(1f) - val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java) - verify(surfaceTransactionApplier, times(1)).scheduleApply(captor.capture()) + val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() + verify(surfaceTransactionApplier, times(1)).scheduleApply( + captorSb.capture { sp -> sp.surface == surfaceControl1}) + val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() + verify(surfaceTransactionApplier, atLeastOnce().description("Wallpaper surface has not " + + "received scheduleApply")).scheduleApply( + captorWp.capture { sp -> sp.surface == surfaceControlWp }) - val params = captor.value + val params = captorSb.getLastValue() // We expect that we've set the surface behind to alpha = 0f since we're not interactive. - assertEquals(params.alpha, 0f) + assertEquals(0f, params.alpha) assertTrue(params.matrix.isIdentity) + assertEquals("Wallpaper surface was expected to have opacity 0", + 0f, captorWp.getLastValue().alpha) verifyNoMoreInteractions(surfaceTransactionApplier) } @@ -317,19 +350,50 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( remoteAnimationTargets, + wallpaperTargets, 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) + keyguardUnlockAnimationController.setWallpaperAppearAmount(1f) + + val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() + verify(surfaceTransactionApplier, times(1)).scheduleApply( + captorSb.capture { sp -> sp.surface == surfaceControl1 }) + val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() + verify(surfaceTransactionApplier, atLeastOnce().description("Wallpaper surface has not " + + "received scheduleApply")).scheduleApply( + captorWp.capture { sp -> sp.surface == surfaceControlWp }) + + val params = captorSb.getLastValue() + assertEquals(1f, params.alpha) assertTrue(params.matrix.isIdentity) + assertEquals("Wallpaper surface was expected to have opacity 1", + 1f, captorWp.getLastValue().alpha) verifyNoMoreInteractions(surfaceTransactionApplier) } + + private class ArgThatCaptor<T> { + private var allArgs: MutableList<T> = mutableListOf() + + fun capture(predicate: Predicate<T>): T { + return argThat{x: T -> + if (predicate.test(x)) { + allArgs.add(x) + return@argThat true + } + return@argThat false + } + } + + fun getLastValue(): T { + return allArgs.last() + } + + fun getAllValues(): List<T> { + return allArgs + } + } } |