diff options
10 files changed, 400 insertions, 357 deletions
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt index 4cf264253bf8..fdb4871423c3 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt @@ -20,6 +20,7 @@ import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator import android.content.Context +import android.graphics.PointF import android.graphics.PorterDuff import android.graphics.PorterDuffXfermode import android.graphics.drawable.GradientDrawable @@ -33,13 +34,13 @@ import android.view.ViewOverlay import android.view.animation.Interpolator import android.window.WindowAnimationState import com.android.app.animation.Interpolators.LINEAR -import com.android.app.animation.MathUtils.max import com.android.internal.annotations.VisibleForTesting import com.android.internal.dynamicanimation.animation.SpringAnimation import com.android.internal.dynamicanimation.animation.SpringForce import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary import java.util.concurrent.Executor import kotlin.math.abs +import kotlin.math.max import kotlin.math.min import kotlin.math.roundToInt @@ -91,6 +92,14 @@ class TransitionAnimator( ) } + /** + * Similar to [getProgress] above, bug the delay and duration are expressed as percentages + * of the animation duration (between 0f and 1f). + */ + internal fun getProgress(linearProgress: Float, delay: Float, duration: Float): Float { + return getProgressInternal(totalDuration = 1f, linearProgress, delay, duration) + } + private fun getProgressInternal( totalDuration: Float, linearProgress: Float, @@ -262,10 +271,10 @@ class TransitionAnimator( var centerY: Float, var scale: Float = 0f, - // Cached values. - var previousCenterX: Float = -1f, - var previousCenterY: Float = -1f, - var previousScale: Float = -1f, + // Update flags (used to decide whether it's time to update the transition state). + var isCenterXUpdated: Boolean = false, + var isCenterYUpdated: Boolean = false, + var isScaleUpdated: Boolean = false, // Completion flags. var isCenterXDone: Boolean = false, @@ -286,6 +295,7 @@ class TransitionAnimator( override fun setValue(state: SpringState, value: Float) { state.centerX = value + state.isCenterXUpdated = true } }, CENTER_Y { @@ -295,6 +305,7 @@ class TransitionAnimator( override fun setValue(state: SpringState, value: Float) { state.centerY = value + state.isCenterYUpdated = true } }, SCALE { @@ -304,6 +315,7 @@ class TransitionAnimator( override fun setValue(state: SpringState, value: Float) { state.scale = value + state.isScaleUpdated = true } }; @@ -444,8 +456,8 @@ class TransitionAnimator( * punching a hole in the [transition container][Controller.transitionContainer]) iff [drawHole] * is true. * - * If [useSpring] is true, a multi-spring animation will be used instead of the default - * interpolators. + * If [startVelocity] (expressed in pixels per second) is not null, a multi-spring animation + * using it for the initial momentum will be used instead of the default interpolators. */ fun startAnimation( controller: Controller, @@ -453,9 +465,9 @@ class TransitionAnimator( windowBackgroundColor: Int, fadeWindowBackgroundLayer: Boolean = true, drawHole: Boolean = false, - useSpring: Boolean = false, + startVelocity: PointF? = null, ): Animation { - if (!controller.isLaunching || useSpring) checkReturnAnimationFrameworkFlag() + if (!controller.isLaunching || startVelocity != null) checkReturnAnimationFrameworkFlag() // We add an extra layer with the same color as the dialog/app splash screen background // color, which is usually the same color of the app background. We first fade in this layer @@ -474,7 +486,7 @@ class TransitionAnimator( windowBackgroundLayer, fadeWindowBackgroundLayer, drawHole, - useSpring, + startVelocity, ) .apply { start() } } @@ -487,7 +499,7 @@ class TransitionAnimator( windowBackgroundLayer: GradientDrawable, fadeWindowBackgroundLayer: Boolean = true, drawHole: Boolean = false, - useSpring: Boolean = false, + startVelocity: PointF? = null, ): Animation { val transitionContainer = controller.transitionContainer val transitionContainerOverlay = transitionContainer.overlay @@ -504,11 +516,12 @@ class TransitionAnimator( openingWindowSyncView != null && openingWindowSyncView.viewRootImpl != controller.transitionContainer.viewRootImpl - return if (useSpring && springTimings != null && springInterpolators != null) { + return if (startVelocity != null && springTimings != null && springInterpolators != null) { createSpringAnimation( controller, startState, endState, + startVelocity, windowBackgroundLayer, transitionContainer, transitionContainerOverlay, @@ -693,6 +706,7 @@ class TransitionAnimator( controller: Controller, startState: State, endState: State, + startVelocity: PointF, windowBackgroundLayer: GradientDrawable, transitionContainer: View, transitionContainerOverlay: ViewGroupOverlay, @@ -721,19 +735,20 @@ class TransitionAnimator( fun updateProgress(state: SpringState) { if ( - (!state.isCenterXDone && state.centerX == state.previousCenterX) || - (!state.isCenterYDone && state.centerY == state.previousCenterY) || - (!state.isScaleDone && state.scale == state.previousScale) + !(state.isCenterXUpdated || state.isCenterXDone) || + !(state.isCenterYUpdated || state.isCenterYDone) || + !(state.isScaleUpdated || state.isScaleDone) ) { // Because all three springs use the same update method, we only actually update - // when all values have changed, avoiding two redundant calls per frame. + // when all properties have received their new value (which could be unchanged from + // the previous one), avoiding two redundant calls per frame. return } - // Update the latest values for the check above. - state.previousCenterX = state.centerX - state.previousCenterY = state.centerY - state.previousScale = state.scale + // Reset the update flags. + state.isCenterXUpdated = false + state.isCenterYUpdated = false + state.isScaleUpdated = false // Current scale-based values, that will be used to find the new animation bounds. val width = @@ -829,6 +844,7 @@ class TransitionAnimator( } setStartValue(startState.centerX) + setStartVelocity(startVelocity.x) setMinValue(min(startState.centerX, endState.centerX)) setMaxValue(max(startState.centerX, endState.centerX)) @@ -850,6 +866,7 @@ class TransitionAnimator( } setStartValue(startState.centerY) + setStartVelocity(startVelocity.y) setMinValue(min(startState.centerY, endState.centerY)) setMaxValue(max(startState.centerY, endState.centerY)) @@ -1057,15 +1074,13 @@ class TransitionAnimator( interpolators = springInterpolators!! val timings = springTimings!! fadeInProgress = - getProgressInternal( - totalDuration = 1f, + getProgress( linearProgress, timings.contentBeforeFadeOutDelay, timings.contentBeforeFadeOutDuration, ) fadeOutProgress = - getProgressInternal( - totalDuration = 1f, + getProgress( linearProgress, timings.contentAfterFadeInDelay, timings.contentAfterFadeInDuration, diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withAnimator.json index aa8044515ea2..aa8044515ea2 100644 --- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching.json +++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withAnimator.json diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withSpring.json index a840d3cb1225..7abff2c74531 100644 --- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching_withSpring.json +++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withSpring.json @@ -33,118 +33,118 @@ "bottom": 0 }, { - "left": 94, - "top": 284, - "right": 206, + "left": 104, + "top": 285, + "right": 215, "bottom": 414 }, { - "left": 83, - "top": 251, - "right": 219, + "left": 92, + "top": 252, + "right": 227, "bottom": 447 }, { - "left": 70, - "top": 212, - "right": 234, - "bottom": 485 + "left": 77, + "top": 213, + "right": 242, + "bottom": 486 }, { - "left": 57, - "top": 173, - "right": 250, - "bottom": 522 + "left": 63, + "top": 175, + "right": 256, + "bottom": 524 }, { - "left": 46, - "top": 139, - "right": 264, - "bottom": 555 + "left": 50, + "top": 141, + "right": 269, + "bottom": 558 }, { - "left": 36, - "top": 109, - "right": 276, - "bottom": 584 + "left": 40, + "top": 112, + "right": 279, + "bottom": 587 }, { - "left": 28, - "top": 84, - "right": 285, - "bottom": 608 + "left": 31, + "top": 88, + "right": 288, + "bottom": 611 }, { - "left": 21, - "top": 65, - "right": 293, - "bottom": 627 + "left": 23, + "top": 68, + "right": 296, + "bottom": 631 }, { - "left": 16, - "top": 49, - "right": 300, - "bottom": 642 + "left": 18, + "top": 53, + "right": 301, + "bottom": 646 }, { - "left": 12, - "top": 36, - "right": 305, - "bottom": 653 + "left": 13, + "top": 41, + "right": 306, + "bottom": 658 }, { - "left": 9, - "top": 27, - "right": 308, - "bottom": 662 + "left": 10, + "top": 31, + "right": 309, + "bottom": 667 }, { "left": 7, - "top": 20, + "top": 24, "right": 312, - "bottom": 669 + "bottom": 673 }, { "left": 5, - "top": 14, + "top": 18, "right": 314, - "bottom": 675 + "bottom": 678 }, { "left": 4, - "top": 11, + "top": 13, "right": 315, - "bottom": 678 + "bottom": 681 }, { "left": 3, - "top": 8, + "top": 10, "right": 316, - "bottom": 681 + "bottom": 684 }, { "left": 2, - "top": 5, + "top": 7, "right": 317, - "bottom": 684 + "bottom": 685 }, { "left": 1, - "top": 4, + "top": 5, "right": 318, - "bottom": 685 + "bottom": 687 }, { "left": 1, - "top": 3, + "top": 4, "right": 318, - "bottom": 686 + "bottom": 688 }, { "left": 0, - "top": 2, + "top": 3, "right": 319, - "bottom": 687 + "bottom": 688 } ] }, @@ -371,5 +371,14 @@ 0 ] } - ] + ], + "\/\/metadata": { + "goldenRepoPath": "frameworks\/base\/packages\/SystemUI\/tests\/goldens\/backgroundAnimation_whenLaunching_withSpring.json", + "goldenIdentifier": "backgroundAnimation_whenLaunching_withSpring", + "testClassName": "TransitionAnimatorTest", + "testMethodName": "backgroundAnimation_whenLaunching[true]", + "deviceLocalPath": "\/data\/user\/0\/com.android.systemui.tests\/files\/platform_screenshots", + "result": "FAILED", + "videoLocation": "TransitionAnimatorTest\/backgroundAnimation_whenLaunching_withSpring.actual.mp4" + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withAnimator.json index aa8044515ea2..aa8044515ea2 100644 --- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning.json +++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withAnimator.json diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withSpring.json index a840d3cb1225..561961145ca1 100644 --- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning_withSpring.json +++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withSpring.json @@ -33,118 +33,118 @@ "bottom": 0 }, { - "left": 94, - "top": 284, - "right": 206, + "left": 104, + "top": 285, + "right": 215, "bottom": 414 }, { - "left": 83, - "top": 251, - "right": 219, + "left": 92, + "top": 252, + "right": 227, "bottom": 447 }, { - "left": 70, - "top": 212, - "right": 234, - "bottom": 485 + "left": 77, + "top": 213, + "right": 242, + "bottom": 486 }, { - "left": 57, - "top": 173, - "right": 250, - "bottom": 522 + "left": 63, + "top": 175, + "right": 256, + "bottom": 524 }, { - "left": 46, - "top": 139, - "right": 264, - "bottom": 555 + "left": 50, + "top": 141, + "right": 269, + "bottom": 558 }, { - "left": 36, - "top": 109, - "right": 276, - "bottom": 584 + "left": 40, + "top": 112, + "right": 279, + "bottom": 587 }, { - "left": 28, - "top": 84, - "right": 285, - "bottom": 608 + "left": 31, + "top": 88, + "right": 288, + "bottom": 611 }, { - "left": 21, - "top": 65, - "right": 293, - "bottom": 627 + "left": 23, + "top": 68, + "right": 296, + "bottom": 631 }, { - "left": 16, - "top": 49, - "right": 300, - "bottom": 642 + "left": 18, + "top": 53, + "right": 301, + "bottom": 646 }, { - "left": 12, - "top": 36, - "right": 305, - "bottom": 653 + "left": 13, + "top": 41, + "right": 306, + "bottom": 658 }, { - "left": 9, - "top": 27, - "right": 308, - "bottom": 662 + "left": 10, + "top": 31, + "right": 309, + "bottom": 667 }, { "left": 7, - "top": 20, + "top": 24, "right": 312, - "bottom": 669 + "bottom": 673 }, { "left": 5, - "top": 14, + "top": 18, "right": 314, - "bottom": 675 + "bottom": 678 }, { "left": 4, - "top": 11, + "top": 13, "right": 315, - "bottom": 678 + "bottom": 681 }, { "left": 3, - "top": 8, + "top": 10, "right": 316, - "bottom": 681 + "bottom": 684 }, { "left": 2, - "top": 5, + "top": 7, "right": 317, - "bottom": 684 + "bottom": 685 }, { "left": 1, - "top": 4, + "top": 5, "right": 318, - "bottom": 685 + "bottom": 687 }, { "left": 1, - "top": 3, + "top": 4, "right": 318, - "bottom": 686 + "bottom": 688 }, { "left": 0, - "top": 2, + "top": 3, "right": 319, - "bottom": 687 + "bottom": 688 } ] }, @@ -371,5 +371,14 @@ 0 ] } - ] + ], + "\/\/metadata": { + "goldenRepoPath": "frameworks\/base\/packages\/SystemUI\/tests\/goldens\/backgroundAnimation_whenReturning_withSpring.json", + "goldenIdentifier": "backgroundAnimation_whenReturning_withSpring", + "testClassName": "TransitionAnimatorTest", + "testMethodName": "backgroundAnimation_whenReturning[true]", + "deviceLocalPath": "\/data\/user\/0\/com.android.systemui.tests\/files\/platform_screenshots", + "result": "FAILED", + "videoLocation": "TransitionAnimatorTest\/backgroundAnimation_whenReturning_withSpring.actual.mp4" + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withAnimator.json index 7f623575fef4..7f623575fef4 100644 --- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching.json +++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withAnimator.json diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withSpring.json index 18eedd450751..825190ba7a32 100644 --- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching_withSpring.json +++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withSpring.json @@ -33,118 +33,118 @@ "bottom": 0 }, { - "left": 94, - "top": 284, - "right": 206, + "left": 104, + "top": 285, + "right": 215, "bottom": 414 }, { - "left": 83, - "top": 251, - "right": 219, + "left": 92, + "top": 252, + "right": 227, "bottom": 447 }, { - "left": 70, - "top": 212, - "right": 234, - "bottom": 485 + "left": 77, + "top": 213, + "right": 242, + "bottom": 486 }, { - "left": 57, - "top": 173, - "right": 250, - "bottom": 522 + "left": 63, + "top": 175, + "right": 256, + "bottom": 524 }, { - "left": 46, - "top": 139, - "right": 264, - "bottom": 555 + "left": 50, + "top": 141, + "right": 269, + "bottom": 558 }, { - "left": 36, - "top": 109, - "right": 276, - "bottom": 584 + "left": 40, + "top": 112, + "right": 279, + "bottom": 587 }, { - "left": 28, - "top": 84, - "right": 285, - "bottom": 608 + "left": 31, + "top": 88, + "right": 288, + "bottom": 611 }, { - "left": 21, - "top": 65, - "right": 293, - "bottom": 627 + "left": 23, + "top": 68, + "right": 296, + "bottom": 631 }, { - "left": 16, - "top": 49, - "right": 300, - "bottom": 642 + "left": 18, + "top": 53, + "right": 301, + "bottom": 646 }, { - "left": 12, - "top": 36, - "right": 305, - "bottom": 653 + "left": 13, + "top": 41, + "right": 306, + "bottom": 658 }, { - "left": 9, - "top": 27, - "right": 308, - "bottom": 662 + "left": 10, + "top": 31, + "right": 309, + "bottom": 667 }, { "left": 7, - "top": 20, + "top": 24, "right": 312, - "bottom": 669 + "bottom": 673 }, { "left": 5, - "top": 14, + "top": 18, "right": 314, - "bottom": 675 + "bottom": 678 }, { "left": 4, - "top": 11, + "top": 13, "right": 315, - "bottom": 678 + "bottom": 681 }, { "left": 3, - "top": 8, + "top": 10, "right": 316, - "bottom": 681 + "bottom": 684 }, { "left": 2, - "top": 5, + "top": 7, "right": 317, - "bottom": 684 + "bottom": 685 }, { "left": 1, - "top": 4, + "top": 5, "right": 318, - "bottom": 685 + "bottom": 687 }, { "left": 1, - "top": 3, + "top": 4, "right": 318, - "bottom": 686 + "bottom": 688 }, { "left": 0, - "top": 2, + "top": 3, "right": 319, - "bottom": 687 + "bottom": 688 } ] }, @@ -371,5 +371,14 @@ 0 ] } - ] + ], + "\/\/metadata": { + "goldenRepoPath": "frameworks\/base\/packages\/SystemUI\/tests\/goldens\/backgroundAnimationWithoutFade_whenLaunching_withSpring.json", + "goldenIdentifier": "backgroundAnimationWithoutFade_whenLaunching_withSpring", + "testClassName": "TransitionAnimatorTest", + "testMethodName": "backgroundAnimationWithoutFade_whenLaunching[true]", + "deviceLocalPath": "\/data\/user\/0\/com.android.systemui.tests\/files\/platform_screenshots", + "result": "FAILED", + "videoLocation": "TransitionAnimatorTest\/backgroundAnimationWithoutFade_whenLaunching_withSpring.actual.mp4" + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withAnimator.json index 98005c53f6e0..98005c53f6e0 100644 --- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning.json +++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withAnimator.json diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withSpring.json index 18eedd450751..63c263175122 100644 --- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning_withSpring.json +++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withSpring.json @@ -33,118 +33,118 @@ "bottom": 0 }, { - "left": 94, - "top": 284, - "right": 206, + "left": 104, + "top": 285, + "right": 215, "bottom": 414 }, { - "left": 83, - "top": 251, - "right": 219, + "left": 92, + "top": 252, + "right": 227, "bottom": 447 }, { - "left": 70, - "top": 212, - "right": 234, - "bottom": 485 + "left": 77, + "top": 213, + "right": 242, + "bottom": 486 }, { - "left": 57, - "top": 173, - "right": 250, - "bottom": 522 + "left": 63, + "top": 175, + "right": 256, + "bottom": 524 }, { - "left": 46, - "top": 139, - "right": 264, - "bottom": 555 + "left": 50, + "top": 141, + "right": 269, + "bottom": 558 }, { - "left": 36, - "top": 109, - "right": 276, - "bottom": 584 + "left": 40, + "top": 112, + "right": 279, + "bottom": 587 }, { - "left": 28, - "top": 84, - "right": 285, - "bottom": 608 + "left": 31, + "top": 88, + "right": 288, + "bottom": 611 }, { - "left": 21, - "top": 65, - "right": 293, - "bottom": 627 + "left": 23, + "top": 68, + "right": 296, + "bottom": 631 }, { - "left": 16, - "top": 49, - "right": 300, - "bottom": 642 + "left": 18, + "top": 53, + "right": 301, + "bottom": 646 }, { - "left": 12, - "top": 36, - "right": 305, - "bottom": 653 + "left": 13, + "top": 41, + "right": 306, + "bottom": 658 }, { - "left": 9, - "top": 27, - "right": 308, - "bottom": 662 + "left": 10, + "top": 31, + "right": 309, + "bottom": 667 }, { "left": 7, - "top": 20, + "top": 24, "right": 312, - "bottom": 669 + "bottom": 673 }, { "left": 5, - "top": 14, + "top": 18, "right": 314, - "bottom": 675 + "bottom": 678 }, { "left": 4, - "top": 11, + "top": 13, "right": 315, - "bottom": 678 + "bottom": 681 }, { "left": 3, - "top": 8, + "top": 10, "right": 316, - "bottom": 681 + "bottom": 684 }, { "left": 2, - "top": 5, + "top": 7, "right": 317, - "bottom": 684 + "bottom": 685 }, { "left": 1, - "top": 4, + "top": 5, "right": 318, - "bottom": 685 + "bottom": 687 }, { "left": 1, - "top": 3, + "top": 4, "right": 318, - "bottom": 686 + "bottom": 688 }, { "left": 0, - "top": 2, + "top": 3, "right": 319, - "bottom": 687 + "bottom": 688 } ] }, @@ -371,5 +371,14 @@ 0 ] } - ] + ], + "\/\/metadata": { + "goldenRepoPath": "frameworks\/base\/packages\/SystemUI\/tests\/goldens\/backgroundAnimationWithoutFade_whenReturning_withSpring.json", + "goldenIdentifier": "backgroundAnimationWithoutFade_whenReturning_withSpring", + "testClassName": "TransitionAnimatorTest", + "testMethodName": "backgroundAnimationWithoutFade_whenReturning[true]", + "deviceLocalPath": "\/data\/user\/0\/com.android.systemui.tests\/files\/platform_screenshots", + "result": "FAILED", + "videoLocation": "TransitionAnimatorTest\/backgroundAnimationWithoutFade_whenReturning_withSpring.actual.mp4" + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt index 288ed4dc9d4f..a1f59c2cc2b5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt @@ -20,18 +20,20 @@ import android.animation.AnimatorRuleRecordingSpec import android.animation.AnimatorTestRuleToolkit import android.animation.MotionControl import android.animation.recordMotion +import android.graphics.Color +import android.graphics.PointF import android.graphics.drawable.GradientDrawable import android.platform.test.annotations.MotionTest import android.view.ViewGroup import android.widget.FrameLayout import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.filters.SmallTest -import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import com.android.systemui.SysuiTestCase import com.android.systemui.activity.EmptyTestActivity import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope +import com.android.systemui.runOnMainThreadAndWaitForIdleSync import kotlin.test.assertTrue import org.junit.Rule import org.junit.Test @@ -47,13 +49,25 @@ import platform.test.screenshot.PathConfig @SmallTest @MotionTest @RunWith(ParameterizedAndroidJunit4::class) -class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() { +class TransitionAnimatorTest( + private val fadeWindowBackgroundLayer: Boolean, + private val isLaunching: Boolean, + private val useSpring: Boolean, +) : SysuiTestCase() { companion object { private const val GOLDENS_PATH = "frameworks/base/packages/SystemUI/tests/goldens" - @get:Parameters(name = "{0}") + @get:Parameters(name = "fadeBackground={0}, isLaunching={1}, useSpring={2}") @JvmStatic - val useSpringValues = booleanArrayOf(false, true).toList() + val parameterValues = buildList { + booleanArrayOf(true, false).forEach { fadeBackground -> + booleanArrayOf(true, false).forEach { isLaunching -> + booleanArrayOf(true, false).forEach { useSpring -> + add(arrayOf(fadeBackground, isLaunching, useSpring)) + } + } + } + } } private val kosmos = Kosmos() @@ -66,11 +80,23 @@ class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() { ActivityTransitionAnimator.SPRING_TIMINGS, ActivityTransitionAnimator.SPRING_INTERPOLATORS, ) - private val withSpring = + private val fade = + if (fadeWindowBackgroundLayer) { + "withFade" + } else { + "withoutFade" + } + private val direction = + if (isLaunching) { + "whenLaunching" + } else { + "whenReturning" + } + private val mode = if (useSpring) { - "_withSpring" + "withSpring" } else { - "" + "withAnimator" } @get:Rule(order = 1) val activityRule = ActivityScenarioRule(EmptyTestActivity::class.java) @@ -83,113 +109,75 @@ class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() { ) @Test - fun backgroundAnimation_whenLaunching() { - val backgroundLayer = GradientDrawable().apply { alpha = 0 } - val animator = - setUpTest(backgroundLayer, isLaunching = true).apply { - getInstrumentation().runOnMainSync { start() } - } + fun backgroundAnimationTimeSeries() { + val transitionContainer = createScene() + val backgroundLayer = createBackgroundLayer() + val animation = createAnimation(transitionContainer, backgroundLayer) - val recordedMotion = recordMotion(backgroundLayer, animator) + val recordedMotion = record(backgroundLayer, animation) motionRule .assertThat(recordedMotion) - .timeSeriesMatchesGolden("backgroundAnimation_whenLaunching$withSpring") + .timeSeriesMatchesGolden("backgroundAnimationTimeSeries_${fade}_${direction}_$mode") } - @Test - fun backgroundAnimation_whenReturning() { - val backgroundLayer = GradientDrawable().apply { alpha = 0 } - val animator = - setUpTest(backgroundLayer, isLaunching = false).apply { - getInstrumentation().runOnMainSync { start() } - } - - val recordedMotion = recordMotion(backgroundLayer, animator) - - motionRule - .assertThat(recordedMotion) - .timeSeriesMatchesGolden("backgroundAnimation_whenReturning$withSpring") - } - - @Test - fun backgroundAnimationWithoutFade_whenLaunching() { - val backgroundLayer = GradientDrawable().apply { alpha = 0 } - val animator = - setUpTest(backgroundLayer, isLaunching = true, fadeWindowBackgroundLayer = false) - .apply { getInstrumentation().runOnMainSync { start() } } - - val recordedMotion = recordMotion(backgroundLayer, animator) - - motionRule - .assertThat(recordedMotion) - .timeSeriesMatchesGolden("backgroundAnimationWithoutFade_whenLaunching$withSpring") - } - - @Test - fun backgroundAnimationWithoutFade_whenReturning() { - val backgroundLayer = GradientDrawable().apply { alpha = 0 } - val animator = - setUpTest(backgroundLayer, isLaunching = false, fadeWindowBackgroundLayer = false) - .apply { getInstrumentation().runOnMainSync { start() } } - - val recordedMotion = recordMotion(backgroundLayer, animator) - - motionRule - .assertThat(recordedMotion) - .timeSeriesMatchesGolden("backgroundAnimationWithoutFade_whenReturning$withSpring") - } - - private fun setUpTest( - backgroundLayer: GradientDrawable, - isLaunching: Boolean, - fadeWindowBackgroundLayer: Boolean = true, - ): TransitionAnimator.Animation { + private fun createScene(): ViewGroup { lateinit var transitionContainer: ViewGroup activityRule.scenario.onActivity { activity -> - transitionContainer = FrameLayout(activity).apply { setBackgroundColor(0x00FF00) } + transitionContainer = FrameLayout(activity) activity.setContentView(transitionContainer) } waitForIdleSync() + return transitionContainer + } + private fun createBackgroundLayer() = + GradientDrawable().apply { + setColor(Color.BLACK) + alpha = 0 + } + + private fun createAnimation( + transitionContainer: ViewGroup, + backgroundLayer: GradientDrawable, + ): TransitionAnimator.Animation { val controller = TestController(transitionContainer, isLaunching) - return transitionAnimator.createAnimation( - controller, - controller.createAnimatorState(), - createEndState(transitionContainer), - backgroundLayer, - fadeWindowBackgroundLayer, - useSpring = useSpring, - ) - } - private fun createEndState(container: ViewGroup): TransitionAnimator.State { val containerLocation = IntArray(2) - container.getLocationOnScreen(containerLocation) - return TransitionAnimator.State( - left = containerLocation[0], - top = containerLocation[1], - right = containerLocation[0] + 320, - bottom = containerLocation[1] + 690, - topCornerRadius = 0f, - bottomCornerRadius = 0f, - ) + transitionContainer.getLocationOnScreen(containerLocation) + val endState = + TransitionAnimator.State( + left = containerLocation[0], + top = containerLocation[1], + right = containerLocation[0] + 320, + bottom = containerLocation[1] + 690, + topCornerRadius = 0f, + bottomCornerRadius = 0f, + ) + + val startVelocity = + if (useSpring) { + PointF(2500f, 30000f) + } else { + null + } + + return transitionAnimator + .createAnimation( + controller, + controller.createAnimatorState(), + endState, + backgroundLayer, + fadeWindowBackgroundLayer, + startVelocity = startVelocity, + ) + .apply { runOnMainThreadAndWaitForIdleSync { start() } } } - private fun recordMotion( + private fun record( backgroundLayer: GradientDrawable, animation: TransitionAnimator.Animation, ): RecordedMotion { - fun record(motionControl: MotionControl, sampleIntervalMs: Long): RecordedMotion { - return motionRule.recordMotion( - AnimatorRuleRecordingSpec(backgroundLayer, motionControl, sampleIntervalMs) { - feature(DrawableFeatureCaptures.bounds, "bounds") - feature(DrawableFeatureCaptures.cornerRadii, "corner_radii") - feature(DrawableFeatureCaptures.alpha, "alpha") - } - ) - } - val motionControl: MotionControl val sampleIntervalMs: Long if (useSpring) { @@ -204,9 +192,13 @@ class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() { sampleIntervalMs = 20L } - var recording: RecordedMotion? = null - getInstrumentation().runOnMainSync { recording = record(motionControl, sampleIntervalMs) } - return recording!! + return motionRule.recordMotion( + AnimatorRuleRecordingSpec(backgroundLayer, motionControl, sampleIntervalMs) { + feature(DrawableFeatureCaptures.bounds, "bounds") + feature(DrawableFeatureCaptures.cornerRadii, "corner_radii") + feature(DrawableFeatureCaptures.alpha, "alpha") + } + ) } } |