diff options
4 files changed, 189 insertions, 169 deletions
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt index 41a00f5237f7..b0c7ac09551a 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt @@ -17,7 +17,6 @@ package com.android.systemui.animation import android.app.ActivityManager -import android.app.ActivityOptions import android.app.ActivityTaskManager import android.app.PendingIntent import android.app.TaskInfo @@ -292,7 +291,7 @@ constructor( ?: throw IllegalStateException( "ActivityTransitionAnimator.callback must be set before using this animator" ) - val runner = createRunner(controller) + val runner = createEphemeralRunner(controller) val runnerDelegate = runner.delegate val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen @@ -416,7 +415,7 @@ constructor( var cleanUpRunnable: Runnable? = null val returnRunner = - createRunner( + createEphemeralRunner( object : DelegateTransitionAnimatorController(launchController) { override val isLaunching = false @@ -458,11 +457,7 @@ constructor( "${launchController.transitionCookie}_returnTransition", ) - transitionRegister?.register( - filter, - transition, - includeTakeover = longLivedReturnAnimationsEnabled(), - ) + transitionRegister?.register(filter, transition, includeTakeover = false) cleanUpRunnable = Runnable { transitionRegister?.unregister(transition) } } @@ -476,12 +471,14 @@ constructor( listeners.remove(listener) } - /** Create a new animation [Runner] controlled by [controller]. */ + /** + * Create a new animation [Runner] controlled by [controller]. + * + * This method must only be used for ephemeral (launch or return) transitions. Otherwise, use + * [createLongLivedRunner]. + */ @VisibleForTesting - @JvmOverloads - fun createRunner(controller: Controller, longLived: Boolean = false): Runner { - if (longLived) assertLongLivedReturnAnimations() - + fun createEphemeralRunner(controller: Controller): Runner { // Make sure we use the modified timings when animating a dialog into an app. val transitionAnimator = if (controller.isDialogLaunch) { @@ -490,7 +487,22 @@ constructor( transitionAnimator } - return Runner(controller, callback!!, transitionAnimator, lifecycleListener, longLived) + return Runner(controller, callback!!, transitionAnimator, lifecycleListener) + } + + /** + * Create a new animation [Runner] controlled by the [Controller] that [controllerFactory] can + * create based on [forLaunch]. + * + * This method must only be used for long-lived registrations. Otherwise, use + * [createEphemeralRunner]. + */ + @VisibleForTesting + fun createLongLivedRunner(controllerFactory: ControllerFactory, forLaunch: Boolean): Runner { + assertLongLivedReturnAnimations() + return Runner(callback!!, transitionAnimator, lifecycleListener) { + controllerFactory.createController(forLaunch) + } } interface PendingIntentStarter { @@ -537,6 +549,23 @@ constructor( } /** + * A factory used to create instances of [Controller] linked to a specific cookie [cookie] and + * [component]. + */ + abstract class ControllerFactory( + val cookie: TransitionCookie, + val component: ComponentName?, + val launchCujType: Int? = null, + val returnCujType: Int? = null, + ) { + /** + * Creates a [Controller] for launching or returning from the activity linked to [cookie] + * and [component]. + */ + abstract fun createController(forLaunch: Boolean): Controller + } + + /** * A controller that takes care of applying the animation to an expanding view. * * Note that all callbacks (onXXX methods) are all called on the main thread. @@ -656,13 +685,13 @@ constructor( } /** - * Registers [controller] as a long-lived transition handler for launch and return animations. + * Registers [controllerFactory] as a long-lived transition handler for launch and return + * animations. * - * The [controller] will only be used for transitions matching the [TransitionCookie] defined - * within it, or the [ComponentName] if the cookie matching fails. Both fields are mandatory for - * this registration. + * The [Controller]s created by [controllerFactory] will only be used for transitions matching + * the [cookie], or the [ComponentName] defined within it if the cookie matching fails. */ - fun register(controller: Controller) { + fun register(cookie: TransitionCookie, controllerFactory: ControllerFactory) { assertLongLivedReturnAnimations() if (transitionRegister == null) { @@ -672,13 +701,8 @@ constructor( ) } - val cookie = - controller.transitionCookie - ?: throw IllegalStateException( - "A cookie must be defined in order to use long-lived animations" - ) val component = - controller.component + controllerFactory.component ?: throw IllegalStateException( "A component must be defined in order to use long-lived animations" ) @@ -699,15 +723,11 @@ constructor( } val launchRemoteTransition = RemoteTransition( - OriginTransition(createRunner(controller, longLived = true)), + OriginTransition(createLongLivedRunner(controllerFactory, forLaunch = true)), "${cookie}_launchTransition", ) transitionRegister.register(launchFilter, launchRemoteTransition, includeTakeover = true) - val returnController = - object : Controller by controller { - override val isLaunching: Boolean = false - } // Cross-task close transitions should not use this animation, so we only register it for // when the opening window is Launcher. val returnFilter = @@ -727,7 +747,7 @@ constructor( } val returnRemoteTransition = RemoteTransition( - OriginTransition(createRunner(returnController, longLived = true)), + OriginTransition(createLongLivedRunner(controllerFactory, forLaunch = false)), "${cookie}_returnTransition", ) transitionRegister.register(returnFilter, returnRemoteTransition, includeTakeover = true) @@ -918,32 +938,61 @@ constructor( } @VisibleForTesting - inner class Runner( + inner class Runner + private constructor( /** * This can hold a reference to a view, so it needs to be cleaned up and can't be held on to - * forever when ![longLived]. + * forever. In case of a long-lived [Runner], this must be null and [controllerFactory] must + * be defined instead. */ private var controller: Controller?, + /** + * Reusable factory to generate single-use controllers. In case of an ephemeral [Runner], + * this must be null and [controller] must be defined instead. + */ + private val controllerFactory: (() -> Controller)?, private val callback: Callback, /** The animator to use to animate the window transition. */ private val transitionAnimator: TransitionAnimator, /** Listener for animation lifecycle events. */ - private val listener: Listener? = null, - /** - * Whether the internal should be kept around after execution for later usage. IMPORTANT: - * should always be false if this [Runner] is to be used directly with [ActivityOptions] - * (i.e. for ephemeral launches), or the controller will leak its view. - */ - private val longLived: Boolean = false, + private val listener: Listener?, ) : IRemoteAnimationRunner.Stub() { + constructor( + controller: Controller, + callback: Callback, + transitionAnimator: TransitionAnimator, + listener: Listener? = null, + ) : this( + controller = controller, + controllerFactory = null, + callback = callback, + transitionAnimator = transitionAnimator, + listener = listener, + ) + + constructor( + callback: Callback, + transitionAnimator: TransitionAnimator, + listener: Listener? = null, + controllerFactory: () -> Controller, + ) : this( + controller = null, + controllerFactory = controllerFactory, + callback = callback, + transitionAnimator = transitionAnimator, + listener = listener, + ) + // This is being passed across IPC boundaries and cycles (through PendingIntentRecords, // etc.) are possible. So we need to make sure we drop any references that might // transitively cause leaks when we're done with animation. @VisibleForTesting var delegate: AnimationDelegate? init { + assert((controller != null).xor(controllerFactory != null)) + delegate = null - if (!longLived) { + if (controller != null) { // Ephemeral launches bundle the runner with the launch request (instead of being // registered ahead of time for later use). This means that there could be a timeout // between creation and invocation, so the delegate needs to exist from the @@ -1021,17 +1070,21 @@ constructor( @AnyThread private fun maybeSetUp() { - if (!longLived || delegate != null) return + if (controllerFactory == null || delegate != null) return createDelegate() } @AnyThread private fun createDelegate() { - if (controller == null) return + var controller = controller + val factory = controllerFactory + if (controller == null && factory == null) return + + controller = controller ?: factory!!.invoke() delegate = AnimationDelegate( mainExecutor, - controller!!, + controller, callback, DelegatingAnimationCompletionListener(listener, this::dispose), transitionAnimator, @@ -1041,13 +1094,12 @@ constructor( @AnyThread fun dispose() { - // Drop references to animation controller once we're done with the animation - // to avoid leaking. + // Drop references to animation controller once we're done with the animation to avoid + // leaking in case of ephemeral launches. When long-lived, [controllerFactory] will + // still be around to create new controllers. mainExecutor.execute { delegate = null - // When long lived, the same Runner can be used more than once. In this case we need - // to keep the controller around so we can rebuild the delegate on demand. - if (!longLived) controller = null + controller = null } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 9f131607cb99..63ac5094c400 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -4058,7 +4058,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { - mRunner = mActivityTransitionAnimator.get().createRunner(mActivityLaunchController); + mRunner = mActivityTransitionAnimator.get() + .createEphemeralRunner(mActivityLaunchController); mRunner.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt index 4bac8f7a1b47..a1fb1a7bb113 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt @@ -110,7 +110,7 @@ constructor( apps: Array<RemoteAnimationTarget>, wallpapers: Array<RemoteAnimationTarget>, nonApps: Array<RemoteAnimationTarget>, - finishedCallback: IRemoteAnimationFinishedCallback? + finishedCallback: IRemoteAnimationFinishedCallback?, ) { Log.d(TAG, "occludeAnimationRunner#onAnimationStart") // Wrap the callback so that it's guaranteed to be nulled out once called. @@ -126,7 +126,7 @@ constructor( taskInfo = apps.firstOrNull()?.taskInfo, ) activityTransitionAnimator - .createRunner(occludeAnimationController) + .createEphemeralRunner(occludeAnimationController) .onAnimationStart( transit, apps, @@ -161,7 +161,7 @@ constructor( apps: Array<RemoteAnimationTarget>, wallpapers: Array<RemoteAnimationTarget>, nonApps: Array<RemoteAnimationTarget>, - finishedCallback: IRemoteAnimationFinishedCallback? + finishedCallback: IRemoteAnimationFinishedCallback?, ) { Log.d(TAG, "unoccludeAnimationRunner#onAnimationStart") // Wrap the callback so that it's guaranteed to be nulled out once called. @@ -179,14 +179,14 @@ constructor( interactionJankMonitor.begin( createInteractionJankMonitorConf( InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION, - "UNOCCLUDE" + "UNOCCLUDE", ) ) if (apps.isEmpty()) { Log.d( TAG, "No apps provided to unocclude runner; " + - "skipping animation and unoccluding." + "skipping animation and unoccluding.", ) unoccludeAnimationFinishedCallback?.onAnimationFinished() return @@ -210,7 +210,7 @@ constructor( 0f, (1f - animatedValue) * surfaceHeight * - UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT + UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT, ) SurfaceParams.Builder(target.leash) @@ -313,12 +313,12 @@ constructor( private fun createInteractionJankMonitorConf( cuj: Int, - tag: String? + tag: String?, ): InteractionJankMonitor.Configuration.Builder { val builder = InteractionJankMonitor.Configuration.Builder.withView( cuj, - keyguardViewController.get().getViewRootImpl().view + keyguardViewController.get().getViewRootImpl().view, ) return if (tag != null) builder.setTag(tag) else builder } diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt index 4aaa82e4a16d..37eb148a5ea7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt @@ -14,6 +14,8 @@ import android.testing.TestableLooper.RunWithLooper import android.view.IRemoteAnimationFinishedCallback import android.view.RemoteAnimationAdapter import android.view.RemoteAnimationTarget +import android.view.RemoteAnimationTarget.MODE_CLOSING +import android.view.RemoteAnimationTarget.MODE_OPENING import android.view.SurfaceControl import android.view.ViewGroup import android.view.WindowManager.TRANSIT_NONE @@ -36,10 +38,6 @@ 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 @@ -49,6 +47,7 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.Mock +import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` @@ -215,22 +214,12 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { ) @Test fun registersLongLivedTransition() { - activityTransitionAnimator.register( - object : DelegateTransitionAnimatorController(controller) { - override val transitionCookie = - ActivityTransitionAnimator.TransitionCookie("test_cookie_1") - override val component = ComponentName("com.test.package", "Test1") - } - ) + var factory = controllerFactory() + activityTransitionAnimator.register(factory.cookie, factory) assertEquals(2, testShellTransitions.remotes.size) - activityTransitionAnimator.register( - object : DelegateTransitionAnimatorController(controller) { - override val transitionCookie = - ActivityTransitionAnimator.TransitionCookie("test_cookie_2") - override val component = ComponentName("com.test.package", "Test2") - } - ) + factory = controllerFactory() + activityTransitionAnimator.register(factory.cookie, factory) assertEquals(4, testShellTransitions.remotes.size) } @@ -241,20 +230,12 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { @Test fun registersLongLivedTransitionOverridingPreviousRegistration() { val cookie = ActivityTransitionAnimator.TransitionCookie("test_cookie") - activityTransitionAnimator.register( - object : DelegateTransitionAnimatorController(controller) { - override val transitionCookie = cookie - override val component = ComponentName("com.test.package", "Test1") - } - ) + var factory = controllerFactory(cookie) + activityTransitionAnimator.register(cookie, factory) val transitions = testShellTransitions.remotes.values.toList() - activityTransitionAnimator.register( - object : DelegateTransitionAnimatorController(controller) { - override val transitionCookie = cookie - override val component = ComponentName("com.test.package", "Test2") - } - ) + factory = controllerFactory(cookie) + activityTransitionAnimator.register(cookie, factory) assertEquals(2, testShellTransitions.remotes.size) for (transition in transitions) { assertThat(testShellTransitions.remotes.values).doesNotContain(transition) @@ -264,38 +245,19 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { @DisableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED) @Test fun doesNotRegisterLongLivedTransitionIfFlagIsDisabled() { - val controller = - object : DelegateTransitionAnimatorController(controller) { - override val transitionCookie = - ActivityTransitionAnimator.TransitionCookie("test_cookie") - override val component = ComponentName("com.test.package", "Test") - } + val factory = controllerFactory(component = null) assertThrows(IllegalStateException::class.java) { - activityTransitionAnimator.register(controller) + activityTransitionAnimator.register(factory.cookie, factory) } } @EnableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED) @Test fun doesNotRegisterLongLivedTransitionIfMissingRequiredProperties() { - // No TransitionCookie - val controllerWithoutCookie = - object : DelegateTransitionAnimatorController(controller) { - override val transitionCookie = null - } - assertThrows(IllegalStateException::class.java) { - activityTransitionAnimator.register(controllerWithoutCookie) - } - // No ComponentName - val controllerWithoutComponent = - object : DelegateTransitionAnimatorController(controller) { - override val transitionCookie = - ActivityTransitionAnimator.TransitionCookie("test_cookie") - override val component = null - } + var factory = controllerFactory(component = null) assertThrows(IllegalStateException::class.java) { - activityTransitionAnimator.register(controllerWithoutComponent) + activityTransitionAnimator.register(factory.cookie, factory) } // No TransitionRegister @@ -307,14 +269,9 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { testTransitionAnimator, disableWmTimeout = true, ) - val validController = - object : DelegateTransitionAnimatorController(controller) { - override val transitionCookie = - ActivityTransitionAnimator.TransitionCookie("test_cookie") - override val component = ComponentName("com.test.package", "Test") - } + factory = controllerFactory() assertThrows(IllegalStateException::class.java) { - activityTransitionAnimator.register(validController) + activityTransitionAnimator.register(factory.cookie, factory) } } @@ -324,18 +281,12 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { ) @Test fun unregistersLongLivedTransition() { - val cookies = arrayOfNulls<ActivityTransitionAnimator.TransitionCookie>(3) for (index in 0 until 3) { - cookies[index] = ActivityTransitionAnimator.TransitionCookie("test_cookie_$index") - - val controller = - object : DelegateTransitionAnimatorController(controller) { - override val transitionCookie = cookies[index] - override val component = ComponentName("foo.bar", "Foobar") - } - activityTransitionAnimator.register(controller) + cookies[index] = mock(ActivityTransitionAnimator.TransitionCookie::class.java) + val factory = controllerFactory(cookies[index]!!) + activityTransitionAnimator.register(factory.cookie, factory) } activityTransitionAnimator.unregister(cookies[0]!!) @@ -350,7 +301,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { @Test fun doesNotStartIfAnimationIsCancelled() { - val runner = activityTransitionAnimator.createRunner(controller) + val runner = activityTransitionAnimator.createEphemeralRunner(controller) runner.onAnimationCancelled() runner.onAnimationStart(TRANSIT_NONE, emptyArray(), emptyArray(), emptyArray(), iCallback) @@ -364,7 +315,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { @Test fun cancelsIfNoOpeningWindowIsFound() { - val runner = activityTransitionAnimator.createRunner(controller) + val runner = activityTransitionAnimator.createEphemeralRunner(controller) runner.onAnimationStart(TRANSIT_NONE, emptyArray(), emptyArray(), emptyArray(), iCallback) waitForIdleSync() @@ -377,7 +328,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { @Test fun startsAnimationIfWindowIsOpening() { - val runner = activityTransitionAnimator.createRunner(controller) + val runner = activityTransitionAnimator.createEphemeralRunner(controller) runner.onAnimationStart( TRANSIT_NONE, arrayOf(fakeWindow()), @@ -404,7 +355,8 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { @Test fun creatingRunnerWithLazyInitializationThrows_whenTheFlagsAreDisabled() { assertThrows(IllegalStateException::class.java) { - activityTransitionAnimator.createRunner(controller, longLived = true) + val factory = controllerFactory() + activityTransitionAnimator.createLongLivedRunner(factory, forLaunch = true) } } @@ -414,7 +366,8 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { ) @Test fun runnerCreatesDelegateLazily_whenPostingTimeouts() { - val runner = activityTransitionAnimator.createRunner(controller, longLived = true) + val factory = controllerFactory() + val runner = activityTransitionAnimator.createLongLivedRunner(factory, forLaunch = true) assertNull(runner.delegate) runner.postTimeouts() assertNotNull(runner.delegate) @@ -426,29 +379,29 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { ) @Test fun runnerCreatesDelegateLazily_onAnimationStart() { - val runner = activityTransitionAnimator.createRunner(controller, longLived = true) + val factory = controllerFactory() + val runner = activityTransitionAnimator.createLongLivedRunner(factory, forLaunch = 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 + activityTransitionAnimator.addListener( + object : ActivityTransitionAnimator.Listener { + override fun onTransitionAnimationStart() { + // This is called iff the delegate was initialized, so it's a good proxy for + // checking the initialization. delegateInitialized = true } } - runner.onAnimationStart( - TRANSIT_NONE, - arrayOf(fakeWindow()), - emptyArray(), - emptyArray(), - iCallback, - ) - initChecker.join() - } + ) + runner.onAnimationStart( + TRANSIT_NONE, + arrayOf(fakeWindow()), + emptyArray(), + emptyArray(), + iCallback, + ) + + waitForIdleSync() assertTrue(delegateInitialized) } @@ -458,28 +411,28 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { ) @Test fun runnerCreatesDelegateLazily_onAnimationTakeover() { - val runner = activityTransitionAnimator.createRunner(controller, longLived = true) + val factory = controllerFactory() + val runner = activityTransitionAnimator.createLongLivedRunner(factory, forLaunch = false) 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 + activityTransitionAnimator.addListener( + object : ActivityTransitionAnimator.Listener { + override fun onTransitionAnimationStart() { + // This is called iff the delegate was initialized, so it's a good proxy for + // checking the initialization. delegateInitialized = true } } - runner.takeOverAnimation( - arrayOf(fakeWindow()), - arrayOf(WindowAnimationState()), - SurfaceControl.Transaction(), - iCallback, - ) - initChecker.join() - } + ) + runner.takeOverAnimation( + arrayOf(fakeWindow(MODE_CLOSING)), + arrayOf(WindowAnimationState()), + SurfaceControl.Transaction(), + iCallback, + ) + + waitForIdleSync() assertTrue(delegateInitialized) } @@ -489,7 +442,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { ) @Test fun animationTakeoverThrows_whenTheFlagsAreDisabled() { - val runner = activityTransitionAnimator.createRunner(controller, longLived = false) + val runner = activityTransitionAnimator.createEphemeralRunner(controller) assertThrows(IllegalStateException::class.java) { runner.takeOverAnimation( arrayOf(fakeWindow()), @@ -506,14 +459,28 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { ) @Test fun disposeRunner_delegateDereferenced() { - val runner = activityTransitionAnimator.createRunner(controller) + val runner = activityTransitionAnimator.createEphemeralRunner(controller) assertNotNull(runner.delegate) runner.dispose() waitForIdleSync() assertNull(runner.delegate) } - private fun fakeWindow(): RemoteAnimationTarget { + private fun controllerFactory( + cookie: ActivityTransitionAnimator.TransitionCookie = + mock(ActivityTransitionAnimator.TransitionCookie::class.java), + component: ComponentName? = mock(ComponentName::class.java), + ): ActivityTransitionAnimator.ControllerFactory { + return object : ActivityTransitionAnimator.ControllerFactory(cookie, component) { + override fun createController(forLaunch: Boolean) = + object : DelegateTransitionAnimatorController(controller) { + override val isLaunching: Boolean + get() = forLaunch + } + } + } + + private fun fakeWindow(mode: Int = MODE_OPENING): RemoteAnimationTarget { val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */) val taskInfo = ActivityManager.RunningTaskInfo() taskInfo.topActivity = ComponentName("com.android.systemui", "FakeActivity") @@ -521,7 +488,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { return RemoteAnimationTarget( 0, - RemoteAnimationTarget.MODE_OPENING, + mode, SurfaceControl(), false, Rect(), |