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

Commit 6cf3d6ba authored by Luca Zuccarini's avatar Luca Zuccarini Committed by Android (Google) Code Review
Browse files

Merge "Introduce animation takeovers to ActivityTransitionAnimator." into main

parents 6648c59d d1847194
Loading
Loading
Loading
Loading
+469 −76

File changed.

Preview size limit exceeded, changes collapsed.

+20 −5
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.internal.dynamicanimation.animation.SpringAnimation
import com.android.internal.dynamicanimation.animation.SpringForce
import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary
import com.android.systemui.shared.Flags.returnAnimationFrameworkLongLived
import java.util.concurrent.Executor
import kotlin.math.abs
import kotlin.math.max
@@ -113,13 +114,26 @@ class TransitionAnimator(
            )
        }

        internal fun checkReturnAnimationFrameworkFlag() {
            check(returnAnimationFrameworkLibrary()) {
                "isLaunching cannot be false when the returnAnimationFrameworkLibrary flag is " +
                    "disabled"
        internal fun assertReturnAnimations() {
            check(returnAnimationsEnabled()) {
                "isLaunching cannot be false when the returnAnimationFrameworkLibrary flag " +
                    "is disabled"
            }
        }

        internal fun returnAnimationsEnabled() = returnAnimationFrameworkLibrary()

        internal fun assertLongLivedReturnAnimations() {
            check(longLivedReturnAnimationsEnabled()) {
                "Long-lived registrations cannot be used when the " +
                    "returnAnimationFrameworkLibrary or the " +
                    "returnAnimationFrameworkLongLived flag are disabled"
            }
        }

        internal fun longLivedReturnAnimationsEnabled() =
            returnAnimationFrameworkLibrary() && returnAnimationFrameworkLongLived()

        internal fun WindowAnimationState.toTransitionState() =
            State().also {
                bounds?.let { b ->
@@ -467,7 +481,8 @@ class TransitionAnimator(
        drawHole: Boolean = false,
        startVelocity: PointF? = null,
    ): Animation {
        if (!controller.isLaunching || startVelocity != null) checkReturnAnimationFrameworkFlag()
        if (!controller.isLaunching) assertReturnAnimations()
        if (startVelocity != null) assertLongLivedReturnAnimations()

        // We add an extra layer with the same color as the dialog/app splash screen background
        // color, which is usually the same color of the app background. We first fade in this layer
+123 −6
Original line number Diff line number Diff line
@@ -16,10 +16,12 @@ import android.view.RemoteAnimationAdapter
import android.view.RemoteAnimationTarget
import android.view.SurfaceControl
import android.view.ViewGroup
import android.view.WindowManager.TRANSIT_NONE
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.window.RemoteTransition
import android.window.TransitionFilter
import android.window.WindowAnimationState
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -34,6 +36,10 @@ import junit.framework.Assert.assertTrue
import junit.framework.AssertionFailedError
import kotlin.concurrent.thread
import kotlin.test.assertEquals
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import org.junit.After
import org.junit.Assert.assertThrows
import org.junit.Before
@@ -258,7 +264,6 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
    @DisableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
    @Test
    fun doesNotRegisterLongLivedTransitionIfFlagIsDisabled() {

        val controller =
            object : DelegateTransitionAnimatorController(controller) {
                override val transitionCookie =
@@ -273,7 +278,6 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
    @EnableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
    @Test
    fun doesNotRegisterLongLivedTransitionIfMissingRequiredProperties() {

        // No TransitionCookie
        val controllerWithoutCookie =
            object : DelegateTransitionAnimatorController(controller) {
@@ -348,7 +352,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
    fun doesNotStartIfAnimationIsCancelled() {
        val runner = activityTransitionAnimator.createRunner(controller)
        runner.onAnimationCancelled()
        runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
        runner.onAnimationStart(TRANSIT_NONE, emptyArray(), emptyArray(), emptyArray(), iCallback)

        waitForIdleSync()
        verify(controller).onTransitionAnimationCancelled()
@@ -361,7 +365,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
    @Test
    fun cancelsIfNoOpeningWindowIsFound() {
        val runner = activityTransitionAnimator.createRunner(controller)
        runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
        runner.onAnimationStart(TRANSIT_NONE, emptyArray(), emptyArray(), emptyArray(), iCallback)

        waitForIdleSync()
        verify(controller).onTransitionAnimationCancelled()
@@ -374,7 +378,13 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
    @Test
    fun startsAnimationIfWindowIsOpening() {
        val runner = activityTransitionAnimator.createRunner(controller)
        runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback)
        runner.onAnimationStart(
            TRANSIT_NONE,
            arrayOf(fakeWindow()),
            emptyArray(),
            emptyArray(),
            iCallback,
        )
        waitForIdleSync()
        verify(listener).onTransitionAnimationStart()
        verify(controller).onTransitionAnimationStart(anyBoolean())
@@ -387,6 +397,113 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
        }
    }

    @DisableFlags(
        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
    )
    @Test
    fun creatingRunnerWithLazyInitializationThrows_whenTheFlagsAreDisabled() {
        assertThrows(IllegalStateException::class.java) {
            activityTransitionAnimator.createRunner(controller, initializeLazily = true)
        }
    }

    @EnableFlags(
        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
    )
    @Test
    fun runnerCreatesDelegateLazily_whenPostingTimeouts() {
        val runner = activityTransitionAnimator.createRunner(controller, initializeLazily = true)
        assertNull(runner.delegate)
        runner.postTimeouts()
        assertNotNull(runner.delegate)
    }

    @EnableFlags(
        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
    )
    @Test
    fun runnerCreatesDelegateLazily_onAnimationStart() {
        val runner = activityTransitionAnimator.createRunner(controller, initializeLazily = true)
        assertNull(runner.delegate)

        // The delegate is cleaned up after execution (which happens in another thread), so what we
        // do instead is check if it becomes non-null at any point with a 1 second timeout. This
        // will tell us that takeOverWithAnimation() triggered the lazy initialization.
        var delegateInitialized = false
        runBlocking {
            val initChecker = launch {
                withTimeout(1.seconds) {
                    while (runner.delegate == null) continue
                    delegateInitialized = true
                }
            }
            runner.onAnimationStart(
                TRANSIT_NONE,
                arrayOf(fakeWindow()),
                emptyArray(),
                emptyArray(),
                iCallback,
            )
            initChecker.join()
        }
        assertTrue(delegateInitialized)
    }

    @EnableFlags(
        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
    )
    @Test
    fun runnerCreatesDelegateLazily_onAnimationTakeover() {
        val runner = activityTransitionAnimator.createRunner(controller, initializeLazily = true)
        assertNull(runner.delegate)

        // The delegate is cleaned up after execution (which happens in another thread), so what we
        // do instead is check if it becomes non-null at any point with a 1 second timeout. This
        // will tell us that takeOverWithAnimation() triggered the lazy initialization.
        var delegateInitialized = false
        runBlocking {
            val initChecker = launch {
                withTimeout(1.seconds) {
                    while (runner.delegate == null) continue
                    delegateInitialized = true
                }
            }
            runner.takeOverAnimation(
                arrayOf(fakeWindow()),
                arrayOf(WindowAnimationState()),
                SurfaceControl.Transaction(),
                iCallback,
            )
            initChecker.join()
        }
        assertTrue(delegateInitialized)
    }

    @DisableFlags(
        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
    )
    @Test
    fun animationTakeoverThrows_whenTheFlagsAreDisabled() {
        val runner = activityTransitionAnimator.createRunner(controller, initializeLazily = false)
        assertThrows(IllegalStateException::class.java) {
            runner.takeOverAnimation(
                arrayOf(fakeWindow()),
                emptyArray(),
                SurfaceControl.Transaction(),
                iCallback,
            )
        }
    }

    @DisableFlags(
        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
    )
    @Test
    fun disposeRunner_delegateDereferenced() {
        val runner = activityTransitionAnimator.createRunner(controller)
@@ -409,7 +526,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
            false,
            Rect(),
            Rect(),
            0,
            1,
            Point(),
            Rect(),
            bounds,