Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c9eadeb6 authored by Marzia Favaro's avatar Marzia Favaro
Browse files

Wallpaper animation for keyguard unlocking.

Fade in alpha animation to switch from lock to home wallpaper window.

Bug: 268136910
Test: KeyguardUnlockAnimationControllerTest

Change-Id: Ia915361c411c592f30b1db9b5c91189998ae26da
parent 4500e0a7
Loading
Loading
Loading
Loading
+73 −0
Original line number Diff line number Diff line
@@ -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
@@ -838,6 +873,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()
+19 −1
Original line number Diff line number Diff line
@@ -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 {
+82 −18
Original line number Diff line number Diff line
@@ -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
        }
    }
}