summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt150
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt189
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(),