Loading packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt +212 −100 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ import android.util.Log import android.util.MathUtils import android.view.View import android.view.ViewGroup import android.view.ViewGroupOverlay import android.view.ViewOverlay import android.view.animation.Interpolator import android.window.WindowAnimationState import androidx.annotation.VisibleForTesting Loading Loading @@ -197,10 +199,24 @@ class TransitionAnimator( } interface Animation { /** Start the animation. */ fun start() /** Cancel the animation. */ fun cancel() } @VisibleForTesting class InterpolatedAnimation(@get:VisibleForTesting val animator: Animator) : Animation { override fun start() { animator.start() } override fun cancel() { animator.cancel() } } /** The timings (durations and delays) used by this animator. */ data class Timings( /** The total duration of the animation. */ Loading Loading @@ -270,33 +286,73 @@ class TransitionAnimator( alpha = 0 } val animator = createAnimator( return createAnimation( controller, controller.createAnimatorState(), endState, windowBackgroundLayer, fadeWindowBackgroundLayer, drawHole, ) animator.start() return object : Animation { override fun cancel() { animator.cancel() } } .apply { start() } } @VisibleForTesting fun createAnimator( fun createAnimation( controller: Controller, startState: State, endState: State, windowBackgroundLayer: GradientDrawable, fadeWindowBackgroundLayer: Boolean = true, drawHole: Boolean = false, ): ValueAnimator { val state = controller.createAnimatorState() ): Animation { val transitionContainer = controller.transitionContainer val transitionContainerOverlay = transitionContainer.overlay val openingWindowSyncView = controller.openingWindowSyncView val openingWindowSyncViewOverlay = openingWindowSyncView?.overlay // Whether we should move the [windowBackgroundLayer] into the overlay of // [Controller.openingWindowSyncView] once the opening app window starts to be visible, or // from it once the closing app window stops being visible. // This is necessary as a one-off sync so we can avoid syncing at every frame, especially // in complex interactions like launching an activity from a dialog. See // b/214961273#comment2 for more details. val moveBackgroundLayerWhenAppVisibilityChanges = openingWindowSyncView != null && openingWindowSyncView.viewRootImpl != controller.transitionContainer.viewRootImpl return createInterpolatedAnimation( controller, startState, endState, windowBackgroundLayer, transitionContainer, transitionContainerOverlay, openingWindowSyncView, openingWindowSyncViewOverlay, fadeWindowBackgroundLayer, drawHole, moveBackgroundLayerWhenAppVisibilityChanges, ) } /** * Creates an interpolator-based animator that uses [timings] and [interpolators] to calculate * the new bounds and corner radiuses at each frame. */ private fun createInterpolatedAnimation( controller: Controller, state: State, endState: State, windowBackgroundLayer: GradientDrawable, transitionContainer: View, transitionContainerOverlay: ViewGroupOverlay, openingWindowSyncView: View? = null, openingWindowSyncViewOverlay: ViewOverlay? = null, fadeWindowBackgroundLayer: Boolean = true, drawHole: Boolean = false, moveBackgroundLayerWhenAppVisibilityChanges: Boolean = false, ): Animation { // Start state. val startTop = state.top val startBottom = state.bottom Loading Loading @@ -333,45 +389,24 @@ class TransitionAnimator( } } val transitionContainer = controller.transitionContainer val isExpandingFullyAbove = isExpandingFullyAbove(transitionContainer, endState) var movedBackgroundLayer = false // Update state. val animator = ValueAnimator.ofFloat(0f, 1f) animator.duration = timings.totalDuration animator.interpolator = LINEAR // Whether we should move the [windowBackgroundLayer] into the overlay of // [Controller.openingWindowSyncView] once the opening app window starts to be visible, or // from it once the closing app window stops being visible. // This is necessary as a one-off sync so we can avoid syncing at every frame, especially // in complex interactions like launching an activity from a dialog. See // b/214961273#comment2 for more details. val openingWindowSyncView = controller.openingWindowSyncView val openingWindowSyncViewOverlay = openingWindowSyncView?.overlay val moveBackgroundLayerWhenAppVisibilityChanges = openingWindowSyncView != null && openingWindowSyncView.viewRootImpl != controller.transitionContainer.viewRootImpl val transitionContainerOverlay = transitionContainer.overlay var movedBackgroundLayer = false animator.addListener( object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator, isReverse: Boolean) { if (DEBUG) { Log.d(TAG, "Animation started") } controller.onTransitionAnimationStart(isExpandingFullyAbove) // Add the drawable to the transition container overlay. Overlays always draw // drawables after views, so we know that it will be drawn above any view added // by the controller. if (controller.isLaunching || openingWindowSyncViewOverlay == null) { transitionContainerOverlay.add(windowBackgroundLayer) } else { openingWindowSyncViewOverlay.add(windowBackgroundLayer) } onAnimationStart( controller, isExpandingFullyAbove, windowBackgroundLayer, transitionContainerOverlay, openingWindowSyncViewOverlay, ) } override fun onAnimationEnd(animation: Animator) { Loading Loading @@ -413,85 +448,162 @@ class TransitionAnimator( state.bottomCornerRadius = MathUtils.lerp(startBottomCornerRadius, endBottomCornerRadius, progress) state.visible = if (controller.isLaunching) { // The expanding view can/should be hidden once it is completely covered by the // opening window. state.visible = checkVisibility(timings, linearProgress, controller.isLaunching) if (!movedBackgroundLayer) { movedBackgroundLayer = maybeMoveBackgroundLayer( controller, state, windowBackgroundLayer, transitionContainer, transitionContainerOverlay, openingWindowSyncView, openingWindowSyncViewOverlay, moveBackgroundLayerWhenAppVisibilityChanges, ) } val container = if (movedBackgroundLayer) { openingWindowSyncView!! } else { controller.transitionContainer } applyStateToWindowBackgroundLayer( windowBackgroundLayer, state, linearProgress, container, fadeWindowBackgroundLayer, drawHole, controller.isLaunching, ) controller.onTransitionAnimationProgress(state, progress, linearProgress) } return InterpolatedAnimation(animator) } private fun onAnimationStart( controller: Controller, isExpandingFullyAbove: Boolean, windowBackgroundLayer: GradientDrawable, transitionContainerOverlay: ViewGroupOverlay, openingWindowSyncViewOverlay: ViewOverlay?, ) { if (DEBUG) { Log.d(TAG, "Animation started") } controller.onTransitionAnimationStart(isExpandingFullyAbove) // Add the drawable to the transition container overlay. Overlays always draw // drawables after views, so we know that it will be drawn above any view added // by the controller. if (controller.isLaunching || openingWindowSyncViewOverlay == null) { transitionContainerOverlay.add(windowBackgroundLayer) } else { openingWindowSyncViewOverlay.add(windowBackgroundLayer) } } private fun onAnimationEnd( controller: Controller, isExpandingFullyAbove: Boolean, windowBackgroundLayer: GradientDrawable, transitionContainerOverlay: ViewGroupOverlay, openingWindowSyncViewOverlay: ViewOverlay?, moveBackgroundLayerWhenAppVisibilityChanges: Boolean, ) { if (DEBUG) { Log.d(TAG, "Animation ended") } // TODO(b/330672236): Post this to the main thread instead so that it does not // flicker with Flexiglass enabled. controller.onTransitionAnimationEnd(isExpandingFullyAbove) transitionContainerOverlay.remove(windowBackgroundLayer) if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) { openingWindowSyncViewOverlay?.remove(windowBackgroundLayer) } } /** Returns whether is the controller's view should be visible with the given [timings]. */ private fun checkVisibility(timings: Timings, progress: Float, isLaunching: Boolean): Boolean { return if (isLaunching) { // The expanding view can/should be hidden once it is completely covered by the opening // window. getProgress( timings, linearProgress, progress, timings.contentBeforeFadeOutDelay, timings.contentBeforeFadeOutDuration, ) < 1 } else { // The shrinking view can/should be hidden while it is completely covered by the closing // window. getProgress( timings, linearProgress, progress, timings.contentAfterFadeInDelay, timings.contentAfterFadeInDuration, ) > 0 } } /** * If necessary, moves the background layer from the view container's overlay to the window sync * view overlay, or vice versa. * * @return true if the background layer vwas moved, false otherwise. */ private fun maybeMoveBackgroundLayer( controller: Controller, state: State, windowBackgroundLayer: GradientDrawable, transitionContainer: View, transitionContainerOverlay: ViewGroupOverlay, openingWindowSyncView: View?, openingWindowSyncViewOverlay: ViewOverlay?, moveBackgroundLayerWhenAppVisibilityChanges: Boolean, ): Boolean { if ( controller.isLaunching && moveBackgroundLayerWhenAppVisibilityChanges && !state.visible && !movedBackgroundLayer controller.isLaunching && moveBackgroundLayerWhenAppVisibilityChanges && !state.visible ) { // The expanding view is not visible, so the opening app is visible. If this is // the first frame when it happens, trigger a one-off sync and move the // background layer in its new container. movedBackgroundLayer = true // The expanding view is not visible, so the opening app is visible. If this is the // first frame when it happens, trigger a one-off sync and move the background layer // in its new container. transitionContainerOverlay.remove(windowBackgroundLayer) openingWindowSyncViewOverlay!!.add(windowBackgroundLayer) ViewRootSync.synchronizeNextDraw( transitionContainer, openingWindowSyncView, openingWindowSyncView!!, then = {}, ) return true } else if ( !controller.isLaunching && moveBackgroundLayerWhenAppVisibilityChanges && state.visible && !movedBackgroundLayer !controller.isLaunching && moveBackgroundLayerWhenAppVisibilityChanges && state.visible ) { // The contracting view is now visible, so the closing app is not. If this is // the first frame when it happens, trigger a one-off sync and move the // background layer in its new container. movedBackgroundLayer = true // The contracting view is now visible, so the closing app is not. If this is the first // frame when it happens, trigger a one-off sync and move the background layer in its // new container. openingWindowSyncViewOverlay!!.remove(windowBackgroundLayer) transitionContainerOverlay.add(windowBackgroundLayer) ViewRootSync.synchronizeNextDraw( openingWindowSyncView, openingWindowSyncView!!, transitionContainer, then = {}, ) } val container = if (movedBackgroundLayer) { openingWindowSyncView!! } else { controller.transitionContainer } applyStateToWindowBackgroundLayer( windowBackgroundLayer, state, linearProgress, container, fadeWindowBackgroundLayer, drawHole, controller.isLaunching, ) controller.onTransitionAnimationProgress(state, progress, linearProgress) return true } return animator return false } /** Return whether we are expanding fully above the [transitionContainer]. */ Loading packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt +14 −20 Original line number Diff line number Diff line Loading @@ -52,14 +52,7 @@ class TransitionAnimatorTest : SysuiTestCase() { private const val GOLDENS_PATH = "frameworks/base/packages/SystemUI/tests/goldens" private val emulationSpec = DeviceEmulationSpec( DisplaySpec( "phone", width = 320, height = 690, densityDpi = 160, ) ) DeviceEmulationSpec(DisplaySpec("phone", width = 320, height = 690, densityDpi = 160)) } private val kosmos = Kosmos() Loading @@ -68,7 +61,7 @@ class TransitionAnimatorTest : SysuiTestCase() { TransitionAnimator( kosmos.fakeExecutor, ActivityTransitionAnimator.TIMINGS, ActivityTransitionAnimator.INTERPOLATORS ActivityTransitionAnimator.INTERPOLATORS, ) @get:Rule(order = 0) val deviceEmulationRule = DeviceEmulationRule(emulationSpec) Loading Loading @@ -131,16 +124,17 @@ class TransitionAnimatorTest : SysuiTestCase() { waitForIdleSync() val controller = TestController(transitionContainer, isLaunching) val animator = transitionAnimator.createAnimator( val animation = transitionAnimator.createAnimation( controller, controller.createAnimatorState(), createEndState(transitionContainer), backgroundLayer, fadeWindowBackgroundLayer ) fadeWindowBackgroundLayer, ) as TransitionAnimator.InterpolatedAnimation return AnimatorSet().apply { duration = animator.duration play(animator) duration = animation.animator.duration play(animation.animator) } } Loading @@ -153,13 +147,13 @@ class TransitionAnimatorTest : SysuiTestCase() { right = containerLocation[0] + emulationSpec.display.width, bottom = containerLocation[1] + emulationSpec.display.height, topCornerRadius = 0f, bottomCornerRadius = 0f bottomCornerRadius = 0f, ) } private fun recordMotion( backgroundLayer: GradientDrawable, animator: AnimatorSet animator: AnimatorSet, ): RecordedMotion { return motionRule.record( animator, Loading @@ -167,7 +161,7 @@ class TransitionAnimatorTest : SysuiTestCase() { feature(DrawableFeatureCaptures.bounds, "bounds") feature(DrawableFeatureCaptures.cornerRadii, "corner_radii") feature(DrawableFeatureCaptures.alpha, "alpha") } }, ) } } Loading @@ -178,7 +172,7 @@ class TransitionAnimatorTest : SysuiTestCase() { */ private class TestController( override var transitionContainer: ViewGroup, override val isLaunching: Boolean override val isLaunching: Boolean, ) : TransitionAnimator.Controller { override fun createAnimatorState(): TransitionAnimator.State { val containerLocation = IntArray(2) Loading @@ -189,7 +183,7 @@ private class TestController( right = containerLocation[0] + 200, bottom = containerLocation[1] + 400, topCornerRadius = 10f, bottomCornerRadius = 20f bottomCornerRadius = 20f, ) } } Loading
packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt +212 −100 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ import android.util.Log import android.util.MathUtils import android.view.View import android.view.ViewGroup import android.view.ViewGroupOverlay import android.view.ViewOverlay import android.view.animation.Interpolator import android.window.WindowAnimationState import androidx.annotation.VisibleForTesting Loading Loading @@ -197,10 +199,24 @@ class TransitionAnimator( } interface Animation { /** Start the animation. */ fun start() /** Cancel the animation. */ fun cancel() } @VisibleForTesting class InterpolatedAnimation(@get:VisibleForTesting val animator: Animator) : Animation { override fun start() { animator.start() } override fun cancel() { animator.cancel() } } /** The timings (durations and delays) used by this animator. */ data class Timings( /** The total duration of the animation. */ Loading Loading @@ -270,33 +286,73 @@ class TransitionAnimator( alpha = 0 } val animator = createAnimator( return createAnimation( controller, controller.createAnimatorState(), endState, windowBackgroundLayer, fadeWindowBackgroundLayer, drawHole, ) animator.start() return object : Animation { override fun cancel() { animator.cancel() } } .apply { start() } } @VisibleForTesting fun createAnimator( fun createAnimation( controller: Controller, startState: State, endState: State, windowBackgroundLayer: GradientDrawable, fadeWindowBackgroundLayer: Boolean = true, drawHole: Boolean = false, ): ValueAnimator { val state = controller.createAnimatorState() ): Animation { val transitionContainer = controller.transitionContainer val transitionContainerOverlay = transitionContainer.overlay val openingWindowSyncView = controller.openingWindowSyncView val openingWindowSyncViewOverlay = openingWindowSyncView?.overlay // Whether we should move the [windowBackgroundLayer] into the overlay of // [Controller.openingWindowSyncView] once the opening app window starts to be visible, or // from it once the closing app window stops being visible. // This is necessary as a one-off sync so we can avoid syncing at every frame, especially // in complex interactions like launching an activity from a dialog. See // b/214961273#comment2 for more details. val moveBackgroundLayerWhenAppVisibilityChanges = openingWindowSyncView != null && openingWindowSyncView.viewRootImpl != controller.transitionContainer.viewRootImpl return createInterpolatedAnimation( controller, startState, endState, windowBackgroundLayer, transitionContainer, transitionContainerOverlay, openingWindowSyncView, openingWindowSyncViewOverlay, fadeWindowBackgroundLayer, drawHole, moveBackgroundLayerWhenAppVisibilityChanges, ) } /** * Creates an interpolator-based animator that uses [timings] and [interpolators] to calculate * the new bounds and corner radiuses at each frame. */ private fun createInterpolatedAnimation( controller: Controller, state: State, endState: State, windowBackgroundLayer: GradientDrawable, transitionContainer: View, transitionContainerOverlay: ViewGroupOverlay, openingWindowSyncView: View? = null, openingWindowSyncViewOverlay: ViewOverlay? = null, fadeWindowBackgroundLayer: Boolean = true, drawHole: Boolean = false, moveBackgroundLayerWhenAppVisibilityChanges: Boolean = false, ): Animation { // Start state. val startTop = state.top val startBottom = state.bottom Loading Loading @@ -333,45 +389,24 @@ class TransitionAnimator( } } val transitionContainer = controller.transitionContainer val isExpandingFullyAbove = isExpandingFullyAbove(transitionContainer, endState) var movedBackgroundLayer = false // Update state. val animator = ValueAnimator.ofFloat(0f, 1f) animator.duration = timings.totalDuration animator.interpolator = LINEAR // Whether we should move the [windowBackgroundLayer] into the overlay of // [Controller.openingWindowSyncView] once the opening app window starts to be visible, or // from it once the closing app window stops being visible. // This is necessary as a one-off sync so we can avoid syncing at every frame, especially // in complex interactions like launching an activity from a dialog. See // b/214961273#comment2 for more details. val openingWindowSyncView = controller.openingWindowSyncView val openingWindowSyncViewOverlay = openingWindowSyncView?.overlay val moveBackgroundLayerWhenAppVisibilityChanges = openingWindowSyncView != null && openingWindowSyncView.viewRootImpl != controller.transitionContainer.viewRootImpl val transitionContainerOverlay = transitionContainer.overlay var movedBackgroundLayer = false animator.addListener( object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator, isReverse: Boolean) { if (DEBUG) { Log.d(TAG, "Animation started") } controller.onTransitionAnimationStart(isExpandingFullyAbove) // Add the drawable to the transition container overlay. Overlays always draw // drawables after views, so we know that it will be drawn above any view added // by the controller. if (controller.isLaunching || openingWindowSyncViewOverlay == null) { transitionContainerOverlay.add(windowBackgroundLayer) } else { openingWindowSyncViewOverlay.add(windowBackgroundLayer) } onAnimationStart( controller, isExpandingFullyAbove, windowBackgroundLayer, transitionContainerOverlay, openingWindowSyncViewOverlay, ) } override fun onAnimationEnd(animation: Animator) { Loading Loading @@ -413,85 +448,162 @@ class TransitionAnimator( state.bottomCornerRadius = MathUtils.lerp(startBottomCornerRadius, endBottomCornerRadius, progress) state.visible = if (controller.isLaunching) { // The expanding view can/should be hidden once it is completely covered by the // opening window. state.visible = checkVisibility(timings, linearProgress, controller.isLaunching) if (!movedBackgroundLayer) { movedBackgroundLayer = maybeMoveBackgroundLayer( controller, state, windowBackgroundLayer, transitionContainer, transitionContainerOverlay, openingWindowSyncView, openingWindowSyncViewOverlay, moveBackgroundLayerWhenAppVisibilityChanges, ) } val container = if (movedBackgroundLayer) { openingWindowSyncView!! } else { controller.transitionContainer } applyStateToWindowBackgroundLayer( windowBackgroundLayer, state, linearProgress, container, fadeWindowBackgroundLayer, drawHole, controller.isLaunching, ) controller.onTransitionAnimationProgress(state, progress, linearProgress) } return InterpolatedAnimation(animator) } private fun onAnimationStart( controller: Controller, isExpandingFullyAbove: Boolean, windowBackgroundLayer: GradientDrawable, transitionContainerOverlay: ViewGroupOverlay, openingWindowSyncViewOverlay: ViewOverlay?, ) { if (DEBUG) { Log.d(TAG, "Animation started") } controller.onTransitionAnimationStart(isExpandingFullyAbove) // Add the drawable to the transition container overlay. Overlays always draw // drawables after views, so we know that it will be drawn above any view added // by the controller. if (controller.isLaunching || openingWindowSyncViewOverlay == null) { transitionContainerOverlay.add(windowBackgroundLayer) } else { openingWindowSyncViewOverlay.add(windowBackgroundLayer) } } private fun onAnimationEnd( controller: Controller, isExpandingFullyAbove: Boolean, windowBackgroundLayer: GradientDrawable, transitionContainerOverlay: ViewGroupOverlay, openingWindowSyncViewOverlay: ViewOverlay?, moveBackgroundLayerWhenAppVisibilityChanges: Boolean, ) { if (DEBUG) { Log.d(TAG, "Animation ended") } // TODO(b/330672236): Post this to the main thread instead so that it does not // flicker with Flexiglass enabled. controller.onTransitionAnimationEnd(isExpandingFullyAbove) transitionContainerOverlay.remove(windowBackgroundLayer) if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) { openingWindowSyncViewOverlay?.remove(windowBackgroundLayer) } } /** Returns whether is the controller's view should be visible with the given [timings]. */ private fun checkVisibility(timings: Timings, progress: Float, isLaunching: Boolean): Boolean { return if (isLaunching) { // The expanding view can/should be hidden once it is completely covered by the opening // window. getProgress( timings, linearProgress, progress, timings.contentBeforeFadeOutDelay, timings.contentBeforeFadeOutDuration, ) < 1 } else { // The shrinking view can/should be hidden while it is completely covered by the closing // window. getProgress( timings, linearProgress, progress, timings.contentAfterFadeInDelay, timings.contentAfterFadeInDuration, ) > 0 } } /** * If necessary, moves the background layer from the view container's overlay to the window sync * view overlay, or vice versa. * * @return true if the background layer vwas moved, false otherwise. */ private fun maybeMoveBackgroundLayer( controller: Controller, state: State, windowBackgroundLayer: GradientDrawable, transitionContainer: View, transitionContainerOverlay: ViewGroupOverlay, openingWindowSyncView: View?, openingWindowSyncViewOverlay: ViewOverlay?, moveBackgroundLayerWhenAppVisibilityChanges: Boolean, ): Boolean { if ( controller.isLaunching && moveBackgroundLayerWhenAppVisibilityChanges && !state.visible && !movedBackgroundLayer controller.isLaunching && moveBackgroundLayerWhenAppVisibilityChanges && !state.visible ) { // The expanding view is not visible, so the opening app is visible. If this is // the first frame when it happens, trigger a one-off sync and move the // background layer in its new container. movedBackgroundLayer = true // The expanding view is not visible, so the opening app is visible. If this is the // first frame when it happens, trigger a one-off sync and move the background layer // in its new container. transitionContainerOverlay.remove(windowBackgroundLayer) openingWindowSyncViewOverlay!!.add(windowBackgroundLayer) ViewRootSync.synchronizeNextDraw( transitionContainer, openingWindowSyncView, openingWindowSyncView!!, then = {}, ) return true } else if ( !controller.isLaunching && moveBackgroundLayerWhenAppVisibilityChanges && state.visible && !movedBackgroundLayer !controller.isLaunching && moveBackgroundLayerWhenAppVisibilityChanges && state.visible ) { // The contracting view is now visible, so the closing app is not. If this is // the first frame when it happens, trigger a one-off sync and move the // background layer in its new container. movedBackgroundLayer = true // The contracting view is now visible, so the closing app is not. If this is the first // frame when it happens, trigger a one-off sync and move the background layer in its // new container. openingWindowSyncViewOverlay!!.remove(windowBackgroundLayer) transitionContainerOverlay.add(windowBackgroundLayer) ViewRootSync.synchronizeNextDraw( openingWindowSyncView, openingWindowSyncView!!, transitionContainer, then = {}, ) } val container = if (movedBackgroundLayer) { openingWindowSyncView!! } else { controller.transitionContainer } applyStateToWindowBackgroundLayer( windowBackgroundLayer, state, linearProgress, container, fadeWindowBackgroundLayer, drawHole, controller.isLaunching, ) controller.onTransitionAnimationProgress(state, progress, linearProgress) return true } return animator return false } /** Return whether we are expanding fully above the [transitionContainer]. */ Loading
packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt +14 −20 Original line number Diff line number Diff line Loading @@ -52,14 +52,7 @@ class TransitionAnimatorTest : SysuiTestCase() { private const val GOLDENS_PATH = "frameworks/base/packages/SystemUI/tests/goldens" private val emulationSpec = DeviceEmulationSpec( DisplaySpec( "phone", width = 320, height = 690, densityDpi = 160, ) ) DeviceEmulationSpec(DisplaySpec("phone", width = 320, height = 690, densityDpi = 160)) } private val kosmos = Kosmos() Loading @@ -68,7 +61,7 @@ class TransitionAnimatorTest : SysuiTestCase() { TransitionAnimator( kosmos.fakeExecutor, ActivityTransitionAnimator.TIMINGS, ActivityTransitionAnimator.INTERPOLATORS ActivityTransitionAnimator.INTERPOLATORS, ) @get:Rule(order = 0) val deviceEmulationRule = DeviceEmulationRule(emulationSpec) Loading Loading @@ -131,16 +124,17 @@ class TransitionAnimatorTest : SysuiTestCase() { waitForIdleSync() val controller = TestController(transitionContainer, isLaunching) val animator = transitionAnimator.createAnimator( val animation = transitionAnimator.createAnimation( controller, controller.createAnimatorState(), createEndState(transitionContainer), backgroundLayer, fadeWindowBackgroundLayer ) fadeWindowBackgroundLayer, ) as TransitionAnimator.InterpolatedAnimation return AnimatorSet().apply { duration = animator.duration play(animator) duration = animation.animator.duration play(animation.animator) } } Loading @@ -153,13 +147,13 @@ class TransitionAnimatorTest : SysuiTestCase() { right = containerLocation[0] + emulationSpec.display.width, bottom = containerLocation[1] + emulationSpec.display.height, topCornerRadius = 0f, bottomCornerRadius = 0f bottomCornerRadius = 0f, ) } private fun recordMotion( backgroundLayer: GradientDrawable, animator: AnimatorSet animator: AnimatorSet, ): RecordedMotion { return motionRule.record( animator, Loading @@ -167,7 +161,7 @@ class TransitionAnimatorTest : SysuiTestCase() { feature(DrawableFeatureCaptures.bounds, "bounds") feature(DrawableFeatureCaptures.cornerRadii, "corner_radii") feature(DrawableFeatureCaptures.alpha, "alpha") } }, ) } } Loading @@ -178,7 +172,7 @@ class TransitionAnimatorTest : SysuiTestCase() { */ private class TestController( override var transitionContainer: ViewGroup, override val isLaunching: Boolean override val isLaunching: Boolean, ) : TransitionAnimator.Controller { override fun createAnimatorState(): TransitionAnimator.State { val containerLocation = IntArray(2) Loading @@ -189,7 +183,7 @@ private class TestController( right = containerLocation[0] + 200, bottom = containerLocation[1] + 400, topCornerRadius = 10f, bottomCornerRadius = 20f bottomCornerRadius = 20f, ) } }