Loading packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt +83 −35 Original line number Diff line number Diff line Loading @@ -73,6 +73,9 @@ import com.android.wm.shell.shared.ShellTransitions import com.android.wm.shell.shared.TransitionUtil import java.util.concurrent.Executor import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeoutOrNull private const val TAG = "ActivityTransitionAnimator" Loading Loading @@ -494,15 +497,19 @@ constructor( /** * Create a new animation [Runner] controlled by the [Controller] that [controllerFactory] can * create based on [forLaunch]. * create based on [forLaunch] and within the given [scope]. * * This method must only be used for long-lived registrations. Otherwise, use * [createEphemeralRunner]. */ @VisibleForTesting fun createLongLivedRunner(controllerFactory: ControllerFactory, forLaunch: Boolean): Runner { fun createLongLivedRunner( controllerFactory: ControllerFactory, scope: CoroutineScope, forLaunch: Boolean, ): Runner { assertLongLivedReturnAnimations() return Runner(callback!!, transitionAnimator, lifecycleListener) { return Runner(scope, callback!!, transitionAnimator, lifecycleListener) { controllerFactory.createController(forLaunch) } } Loading Loading @@ -564,7 +571,7 @@ constructor( * Creates a [Controller] for launching or returning from the activity linked to [cookie] * and [component]. */ abstract fun createController(forLaunch: Boolean): Controller abstract suspend fun createController(forLaunch: Boolean): Controller } /** Loading Loading @@ -691,9 +698,14 @@ constructor( * animations. * * 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. * the [cookie], or the [ComponentName] defined within it if the cookie matching fails. These * [Controller]s can only be created within [scope]. */ fun register(cookie: TransitionCookie, controllerFactory: ControllerFactory) { fun register( cookie: TransitionCookie, controllerFactory: ControllerFactory, scope: CoroutineScope, ) { assertLongLivedReturnAnimations() if (transitionRegister == null) { Loading Loading @@ -725,7 +737,7 @@ constructor( } val launchRemoteTransition = RemoteTransition( OriginTransition(createLongLivedRunner(controllerFactory, forLaunch = true)), OriginTransition(createLongLivedRunner(controllerFactory, scope, forLaunch = true)), "${cookie}_launchTransition", ) transitionRegister.register(launchFilter, launchRemoteTransition, includeTakeover = true) Loading @@ -749,7 +761,9 @@ constructor( } val returnRemoteTransition = RemoteTransition( OriginTransition(createLongLivedRunner(controllerFactory, forLaunch = false)), OriginTransition( createLongLivedRunner(controllerFactory, scope, forLaunch = false) ), "${cookie}_returnTransition", ) transitionRegister.register(returnFilter, returnRemoteTransition, includeTakeover = true) Loading Loading @@ -952,7 +966,9 @@ constructor( * 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 controllerFactory: (suspend () -> Controller)?, /** The scope to use when this runner is based on [controllerFactory]. */ private val scope: CoroutineScope? = null, private val callback: Callback, /** The animator to use to animate the window transition. */ private val transitionAnimator: TransitionAnimator, Loading @@ -973,13 +989,15 @@ constructor( ) constructor( scope: CoroutineScope, callback: Callback, transitionAnimator: TransitionAnimator, listener: Listener? = null, controllerFactory: () -> Controller, controllerFactory: suspend () -> Controller, ) : this( controller = null, controllerFactory = controllerFactory, scope = scope, callback = callback, transitionAnimator = transitionAnimator, listener = listener, Loading @@ -994,12 +1012,12 @@ constructor( assert((controller != null).xor(controllerFactory != null)) delegate = null if (controller != null) { controller?.let { // 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 // beginning in order to handle such timeout. createDelegate() createDelegate(it) } } Loading Loading @@ -1040,49 +1058,79 @@ constructor( finishedCallback: IRemoteAnimationFinishedCallback?, performAnimation: (AnimationDelegate) -> Unit, ) { maybeSetUp() val delegate = delegate mainExecutor.execute { if (delegate == null) { Log.i(TAG, "onAnimationStart called after completion") // Animation started too late and timed out already. We need to still // signal back that we're done with it. finishedCallback?.onAnimationFinished() val controller = controller val controllerFactory = controllerFactory if (controller != null) { maybeSetUp(controller) val success = startAnimation(performAnimation) if (!success) finishedCallback?.onAnimationFinished() } else if (controllerFactory != null) { scope?.launch { val success = withTimeoutOrNull(TRANSITION_TIMEOUT) { setUp(controllerFactory) startAnimation(performAnimation) } ?: false if (!success) finishedCallback?.onAnimationFinished() } } else { performAnimation(delegate) // This should never happen, as either the controller or factory should always be // defined. This final call is for safety in case something goes wrong. Log.wtf(TAG, "initAndRun with neither a controller nor factory") finishedCallback?.onAnimationFinished() } } /** Tries to start the animation on the main thread and returns whether it succeeded. */ @BinderThread private fun startAnimation(performAnimation: (AnimationDelegate) -> Unit): Boolean { val delegate = delegate return if (delegate != null) { mainExecutor.execute { performAnimation(delegate) } true } else { // Animation started too late and timed out already. Log.i(TAG, "startAnimation called after completion") false } } @BinderThread override fun onAnimationCancelled() { val delegate = delegate mainExecutor.execute { delegate ?: Log.wtf(TAG, "onAnimationCancelled called after completion") delegate?.onAnimationCancelled() if (delegate != null) { mainExecutor.execute { delegate.onAnimationCancelled() } } else { Log.wtf(TAG, "onAnimationCancelled called after completion") } } /** * Posts the default animation timeouts. Since this only applies to ephemeral launches, this * method is a no-op if [controller] is not defined. */ @VisibleForTesting @UiThread fun postTimeouts() { maybeSetUp() controller?.let { maybeSetUp(it) } delegate?.postTimeouts() } @AnyThread private fun maybeSetUp() { if (controllerFactory == null || delegate != null) return createDelegate() private fun maybeSetUp(controller: Controller) { if (delegate != null) return createDelegate(controller) } @AnyThread private fun createDelegate() { var controller = controller val factory = controllerFactory if (controller == null && factory == null) return private suspend fun setUp(createController: suspend () -> Controller) { val controller = createController() createDelegate(controller) } controller = controller ?: factory!!.invoke() @AnyThread private fun createDelegate(controller: Controller) { delegate = AnimationDelegate( mainExecutor, Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt +25 −10 Original line number Diff line number Diff line Loading @@ -25,11 +25,14 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.testScope import com.android.systemui.shared.Flags as SharedFlags import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith Loading @@ -48,6 +51,7 @@ class ActivityStarterImplTest : SysuiTestCase() { @Mock private lateinit var activityStarterInternal: ActivityStarterInternalImpl @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController private lateinit var underTest: ActivityStarterImpl private val kosmos = testKosmos() private val mainExecutor = FakeExecutor(FakeSystemClock()) @Before Loading @@ -69,12 +73,18 @@ class ActivityStarterImplTest : SysuiTestCase() { @EnableSceneContainer @Test fun registerTransition_forwardsTheRequest() { with(kosmos) { testScope.runTest { val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) underTest.registerTransition(cookie, controllerFactory) underTest.registerTransition(cookie, controllerFactory, testScope) verify(activityStarterInternal).registerTransition(eq(cookie), eq(controllerFactory)) verify(activityStarterInternal) .registerTransition(eq(cookie), eq(controllerFactory), eq(testScope)) } } } @DisableFlags( Loading @@ -83,12 +93,17 @@ class ActivityStarterImplTest : SysuiTestCase() { ) @Test fun registerTransition_doesNotForwardTheRequest_whenFlaggedOff() { with(kosmos) { testScope.runTest { val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) underTest.registerTransition(cookie, controllerFactory) underTest.registerTransition(cookie, controllerFactory, testScope) verify(activityStarterInternal, never()).registerTransition(any(), any()) verify(activityStarterInternal, never()).registerTransition(any(), any(), any()) } } } @EnableFlags( Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt +25 −11 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import com.android.systemui.communal.domain.interactor.CommunalSettingsInteracto import com.android.systemui.communal.domain.interactor.communalSettingsInteractor import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.settings.UserTracker import com.android.systemui.shade.ShadeController Loading @@ -58,12 +59,14 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Test Loading Loading @@ -109,6 +112,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { @Mock private lateinit var communalSceneInteractor: CommunalSceneInteractor @Mock private lateinit var communalSettingsInteractor: CommunalSettingsInteractor private lateinit var underTest: LegacyActivityStarterInternalImpl private val kosmos = testKosmos() private val mainExecutor = FakeExecutor(FakeSystemClock()) private val shadeAnimationInteractor = ShadeAnimationInteractorLegacyImpl(ShadeAnimationRepository(), FakeShadeRepository()) Loading Loading @@ -157,13 +161,18 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { ) @Test fun registerTransition_registers() { with(kosmos) { testScope.runTest { val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) `when`(controllerFactory.cookie).thenReturn(cookie) underTest.registerTransition(cookie, controllerFactory) underTest.registerTransition(cookie, controllerFactory, testScope) verify(activityTransitionAnimator).register(eq(cookie), any()) verify(activityTransitionAnimator).register(eq(cookie), any(), eq(testScope)) } } } @DisableFlags( Loading @@ -172,14 +181,19 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { ) @Test fun registerTransition_throws_whenFlagsAreDisabled() { with(kosmos) { testScope.runTest { val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) assertThrows(IllegalStateException::class.java) { underTest.registerTransition(cookie, controllerFactory) underTest.registerTransition(cookie, controllerFactory, testScope) } verify(activityTransitionAnimator, never()).register(any(), any()) verify(activityTransitionAnimator, never()).register(any(), any(), any()) } } } @EnableFlags( Loading packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +5 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import android.view.View; import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.plugins.annotations.ProvidesInterface; import kotlinx.coroutines.CoroutineScope; /** * An interface to start activities. This is used as a callback from the views to * {@link PhoneStatusBar} to allow custom handling for starting the activity, i.e. dismissing the Loading @@ -37,11 +39,12 @@ public interface ActivityStarter { /** * Registers the given {@link ActivityTransitionAnimator.ControllerFactory} for launching and * closing transitions matching the {@link ActivityTransitionAnimator.TransitionCookie} and the * {@link ComponentName} that it contains. * {@link ComponentName} that it contains, within the given {@link CoroutineScope}. */ void registerTransition( ActivityTransitionAnimator.TransitionCookie cookie, ActivityTransitionAnimator.ControllerFactory controllerFactory); ActivityTransitionAnimator.ControllerFactory controllerFactory, CoroutineScope scope); /** * Unregisters the {@link ActivityTransitionAnimator.ControllerFactory} previously registered Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt +3 −1 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.util.concurrency.DelayableExecutor import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.CoroutineScope /** Handles start activity logic in SystemUI. */ @SysUISingleton Loading @@ -52,9 +53,10 @@ constructor( override fun registerTransition( cookie: ActivityTransitionAnimator.TransitionCookie, controllerFactory: ActivityTransitionAnimator.ControllerFactory, scope: CoroutineScope, ) { if (!TransitionAnimator.longLivedReturnAnimationsEnabled()) return activityStarterInternal.registerTransition(cookie, controllerFactory) activityStarterInternal.registerTransition(cookie, controllerFactory, scope) } override fun unregisterTransition(cookie: ActivityTransitionAnimator.TransitionCookie) { Loading Loading
packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt +83 −35 Original line number Diff line number Diff line Loading @@ -73,6 +73,9 @@ import com.android.wm.shell.shared.ShellTransitions import com.android.wm.shell.shared.TransitionUtil import java.util.concurrent.Executor import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeoutOrNull private const val TAG = "ActivityTransitionAnimator" Loading Loading @@ -494,15 +497,19 @@ constructor( /** * Create a new animation [Runner] controlled by the [Controller] that [controllerFactory] can * create based on [forLaunch]. * create based on [forLaunch] and within the given [scope]. * * This method must only be used for long-lived registrations. Otherwise, use * [createEphemeralRunner]. */ @VisibleForTesting fun createLongLivedRunner(controllerFactory: ControllerFactory, forLaunch: Boolean): Runner { fun createLongLivedRunner( controllerFactory: ControllerFactory, scope: CoroutineScope, forLaunch: Boolean, ): Runner { assertLongLivedReturnAnimations() return Runner(callback!!, transitionAnimator, lifecycleListener) { return Runner(scope, callback!!, transitionAnimator, lifecycleListener) { controllerFactory.createController(forLaunch) } } Loading Loading @@ -564,7 +571,7 @@ constructor( * Creates a [Controller] for launching or returning from the activity linked to [cookie] * and [component]. */ abstract fun createController(forLaunch: Boolean): Controller abstract suspend fun createController(forLaunch: Boolean): Controller } /** Loading Loading @@ -691,9 +698,14 @@ constructor( * animations. * * 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. * the [cookie], or the [ComponentName] defined within it if the cookie matching fails. These * [Controller]s can only be created within [scope]. */ fun register(cookie: TransitionCookie, controllerFactory: ControllerFactory) { fun register( cookie: TransitionCookie, controllerFactory: ControllerFactory, scope: CoroutineScope, ) { assertLongLivedReturnAnimations() if (transitionRegister == null) { Loading Loading @@ -725,7 +737,7 @@ constructor( } val launchRemoteTransition = RemoteTransition( OriginTransition(createLongLivedRunner(controllerFactory, forLaunch = true)), OriginTransition(createLongLivedRunner(controllerFactory, scope, forLaunch = true)), "${cookie}_launchTransition", ) transitionRegister.register(launchFilter, launchRemoteTransition, includeTakeover = true) Loading @@ -749,7 +761,9 @@ constructor( } val returnRemoteTransition = RemoteTransition( OriginTransition(createLongLivedRunner(controllerFactory, forLaunch = false)), OriginTransition( createLongLivedRunner(controllerFactory, scope, forLaunch = false) ), "${cookie}_returnTransition", ) transitionRegister.register(returnFilter, returnRemoteTransition, includeTakeover = true) Loading Loading @@ -952,7 +966,9 @@ constructor( * 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 controllerFactory: (suspend () -> Controller)?, /** The scope to use when this runner is based on [controllerFactory]. */ private val scope: CoroutineScope? = null, private val callback: Callback, /** The animator to use to animate the window transition. */ private val transitionAnimator: TransitionAnimator, Loading @@ -973,13 +989,15 @@ constructor( ) constructor( scope: CoroutineScope, callback: Callback, transitionAnimator: TransitionAnimator, listener: Listener? = null, controllerFactory: () -> Controller, controllerFactory: suspend () -> Controller, ) : this( controller = null, controllerFactory = controllerFactory, scope = scope, callback = callback, transitionAnimator = transitionAnimator, listener = listener, Loading @@ -994,12 +1012,12 @@ constructor( assert((controller != null).xor(controllerFactory != null)) delegate = null if (controller != null) { controller?.let { // 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 // beginning in order to handle such timeout. createDelegate() createDelegate(it) } } Loading Loading @@ -1040,49 +1058,79 @@ constructor( finishedCallback: IRemoteAnimationFinishedCallback?, performAnimation: (AnimationDelegate) -> Unit, ) { maybeSetUp() val delegate = delegate mainExecutor.execute { if (delegate == null) { Log.i(TAG, "onAnimationStart called after completion") // Animation started too late and timed out already. We need to still // signal back that we're done with it. finishedCallback?.onAnimationFinished() val controller = controller val controllerFactory = controllerFactory if (controller != null) { maybeSetUp(controller) val success = startAnimation(performAnimation) if (!success) finishedCallback?.onAnimationFinished() } else if (controllerFactory != null) { scope?.launch { val success = withTimeoutOrNull(TRANSITION_TIMEOUT) { setUp(controllerFactory) startAnimation(performAnimation) } ?: false if (!success) finishedCallback?.onAnimationFinished() } } else { performAnimation(delegate) // This should never happen, as either the controller or factory should always be // defined. This final call is for safety in case something goes wrong. Log.wtf(TAG, "initAndRun with neither a controller nor factory") finishedCallback?.onAnimationFinished() } } /** Tries to start the animation on the main thread and returns whether it succeeded. */ @BinderThread private fun startAnimation(performAnimation: (AnimationDelegate) -> Unit): Boolean { val delegate = delegate return if (delegate != null) { mainExecutor.execute { performAnimation(delegate) } true } else { // Animation started too late and timed out already. Log.i(TAG, "startAnimation called after completion") false } } @BinderThread override fun onAnimationCancelled() { val delegate = delegate mainExecutor.execute { delegate ?: Log.wtf(TAG, "onAnimationCancelled called after completion") delegate?.onAnimationCancelled() if (delegate != null) { mainExecutor.execute { delegate.onAnimationCancelled() } } else { Log.wtf(TAG, "onAnimationCancelled called after completion") } } /** * Posts the default animation timeouts. Since this only applies to ephemeral launches, this * method is a no-op if [controller] is not defined. */ @VisibleForTesting @UiThread fun postTimeouts() { maybeSetUp() controller?.let { maybeSetUp(it) } delegate?.postTimeouts() } @AnyThread private fun maybeSetUp() { if (controllerFactory == null || delegate != null) return createDelegate() private fun maybeSetUp(controller: Controller) { if (delegate != null) return createDelegate(controller) } @AnyThread private fun createDelegate() { var controller = controller val factory = controllerFactory if (controller == null && factory == null) return private suspend fun setUp(createController: suspend () -> Controller) { val controller = createController() createDelegate(controller) } controller = controller ?: factory!!.invoke() @AnyThread private fun createDelegate(controller: Controller) { delegate = AnimationDelegate( mainExecutor, Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt +25 −10 Original line number Diff line number Diff line Loading @@ -25,11 +25,14 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.testScope import com.android.systemui.shared.Flags as SharedFlags import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith Loading @@ -48,6 +51,7 @@ class ActivityStarterImplTest : SysuiTestCase() { @Mock private lateinit var activityStarterInternal: ActivityStarterInternalImpl @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController private lateinit var underTest: ActivityStarterImpl private val kosmos = testKosmos() private val mainExecutor = FakeExecutor(FakeSystemClock()) @Before Loading @@ -69,12 +73,18 @@ class ActivityStarterImplTest : SysuiTestCase() { @EnableSceneContainer @Test fun registerTransition_forwardsTheRequest() { with(kosmos) { testScope.runTest { val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) underTest.registerTransition(cookie, controllerFactory) underTest.registerTransition(cookie, controllerFactory, testScope) verify(activityStarterInternal).registerTransition(eq(cookie), eq(controllerFactory)) verify(activityStarterInternal) .registerTransition(eq(cookie), eq(controllerFactory), eq(testScope)) } } } @DisableFlags( Loading @@ -83,12 +93,17 @@ class ActivityStarterImplTest : SysuiTestCase() { ) @Test fun registerTransition_doesNotForwardTheRequest_whenFlaggedOff() { with(kosmos) { testScope.runTest { val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) underTest.registerTransition(cookie, controllerFactory) underTest.registerTransition(cookie, controllerFactory, testScope) verify(activityStarterInternal, never()).registerTransition(any(), any()) verify(activityStarterInternal, never()).registerTransition(any(), any(), any()) } } } @EnableFlags( Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt +25 −11 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import com.android.systemui.communal.domain.interactor.CommunalSettingsInteracto import com.android.systemui.communal.domain.interactor.communalSettingsInteractor import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.settings.UserTracker import com.android.systemui.shade.ShadeController Loading @@ -58,12 +59,14 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Test Loading Loading @@ -109,6 +112,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { @Mock private lateinit var communalSceneInteractor: CommunalSceneInteractor @Mock private lateinit var communalSettingsInteractor: CommunalSettingsInteractor private lateinit var underTest: LegacyActivityStarterInternalImpl private val kosmos = testKosmos() private val mainExecutor = FakeExecutor(FakeSystemClock()) private val shadeAnimationInteractor = ShadeAnimationInteractorLegacyImpl(ShadeAnimationRepository(), FakeShadeRepository()) Loading Loading @@ -157,13 +161,18 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { ) @Test fun registerTransition_registers() { with(kosmos) { testScope.runTest { val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) `when`(controllerFactory.cookie).thenReturn(cookie) underTest.registerTransition(cookie, controllerFactory) underTest.registerTransition(cookie, controllerFactory, testScope) verify(activityTransitionAnimator).register(eq(cookie), any()) verify(activityTransitionAnimator).register(eq(cookie), any(), eq(testScope)) } } } @DisableFlags( Loading @@ -172,14 +181,19 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { ) @Test fun registerTransition_throws_whenFlagsAreDisabled() { with(kosmos) { testScope.runTest { val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java) assertThrows(IllegalStateException::class.java) { underTest.registerTransition(cookie, controllerFactory) underTest.registerTransition(cookie, controllerFactory, testScope) } verify(activityTransitionAnimator, never()).register(any(), any()) verify(activityTransitionAnimator, never()).register(any(), any(), any()) } } } @EnableFlags( Loading
packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +5 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import android.view.View; import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.plugins.annotations.ProvidesInterface; import kotlinx.coroutines.CoroutineScope; /** * An interface to start activities. This is used as a callback from the views to * {@link PhoneStatusBar} to allow custom handling for starting the activity, i.e. dismissing the Loading @@ -37,11 +39,12 @@ public interface ActivityStarter { /** * Registers the given {@link ActivityTransitionAnimator.ControllerFactory} for launching and * closing transitions matching the {@link ActivityTransitionAnimator.TransitionCookie} and the * {@link ComponentName} that it contains. * {@link ComponentName} that it contains, within the given {@link CoroutineScope}. */ void registerTransition( ActivityTransitionAnimator.TransitionCookie cookie, ActivityTransitionAnimator.ControllerFactory controllerFactory); ActivityTransitionAnimator.ControllerFactory controllerFactory, CoroutineScope scope); /** * Unregisters the {@link ActivityTransitionAnimator.ControllerFactory} previously registered Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt +3 −1 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.util.concurrency.DelayableExecutor import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.CoroutineScope /** Handles start activity logic in SystemUI. */ @SysUISingleton Loading @@ -52,9 +53,10 @@ constructor( override fun registerTransition( cookie: ActivityTransitionAnimator.TransitionCookie, controllerFactory: ActivityTransitionAnimator.ControllerFactory, scope: CoroutineScope, ) { if (!TransitionAnimator.longLivedReturnAnimationsEnabled()) return activityStarterInternal.registerTransition(cookie, controllerFactory) activityStarterInternal.registerTransition(cookie, controllerFactory, scope) } override fun unregisterTransition(cookie: ActivityTransitionAnimator.TransitionCookie) { Loading