diff options
| author | 2024-08-21 10:50:56 +0000 | |
|---|---|---|
| committer | 2024-08-21 10:50:56 +0000 | |
| commit | 9170c9391f73b2e2215dcbf11ca31d0875e2177a (patch) | |
| tree | a244787868ff500b5c413d154266d97c784d8f4d | |
| parent | 6fd37ffbc95711c5a942b31c20d3074d815c37c6 (diff) | |
| parent | 2307093dc1aef8490efb8bf5b582a67dfe4b9c70 (diff) | |
Merge "STL: Add defaultOverscrollProgressConverter in SceneTransitions [1/2]" into main
5 files changed, 109 insertions, 5 deletions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index c5c3a62608b5..e6198148d265 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -1106,7 +1106,10 @@ private inline fun <T> computeValue( val directionSign = if (transition.isUpOrLeft) -1 else 1 val isToContent = overscroll.scene == transition.toContent val linearProgress = transition.progress.let { if (isToContent) it - 1f else it } - val progress = directionSign * overscroll.progressConverter.convert(linearProgress) + val progressConverter = + overscroll.progressConverter + ?: layoutImpl.state.transitions.defaultProgressConverter + val progress = directionSign * progressConverter.convert(linearProgress) val rangeProgress = propertySpec.range?.progress(progress) ?: progress // Interpolate between the value at rest and the over scrolled value. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt index a063438bb911..d35d95685d22 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt @@ -45,6 +45,7 @@ internal constructor( internal val transitionSpecs: List<TransitionSpecImpl>, internal val overscrollSpecs: List<OverscrollSpecImpl>, internal val interruptionHandler: InterruptionHandler, + internal val defaultProgressConverter: ProgressConverter, ) { private val transitionCache = mutableMapOf< @@ -147,6 +148,7 @@ internal constructor( transitionSpecs = emptyList(), overscrollSpecs = emptyList(), interruptionHandler = DefaultInterruptionHandler, + defaultProgressConverter = ProgressConverter.Default, ) } } @@ -282,14 +284,14 @@ interface OverscrollSpec { * - 1, the user overscrolled by exactly the [OverscrollBuilder.distance]. * - Greater than 1, the user overscrolled more than the [OverscrollBuilder.distance]. */ - val progressConverter: ProgressConverter + val progressConverter: ProgressConverter? } internal class OverscrollSpecImpl( override val scene: SceneKey, override val orientation: Orientation, override val transformationSpec: TransformationSpecImpl, - override val progressConverter: ProgressConverter, + override val progressConverter: ProgressConverter?, ) : OverscrollSpec /** diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt index ad1fd96c0b47..e38c849182d8 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt @@ -50,6 +50,12 @@ interface SceneTransitionsBuilder { var interruptionHandler: InterruptionHandler /** + * Default [ProgressConverter] used during overscroll. It lets you change a linear progress into + * a function of your choice. Defaults to [ProgressConverter.Default]. + */ + var defaultOverscrollProgressConverter: ProgressConverter + + /** * Define the default animation to be played when transitioning [to] the specified content, from * any content. For the animation specification to apply only when transitioning between two * specific contents, use [from] instead. @@ -217,7 +223,7 @@ interface OverscrollBuilder : BaseTransitionBuilder { * - 1, the user overscrolled by exactly the [distance]. * - Greater than 1, the user overscrolled more than the [distance]. */ - var progressConverter: ProgressConverter + var progressConverter: ProgressConverter? /** Translate the element(s) matching [matcher] by ([x], [y]) pixels. */ fun translate( diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt index 771d1dd45e02..523e5bdd7203 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt @@ -50,12 +50,14 @@ internal fun transitionsImpl( impl.transitionSpecs, impl.transitionOverscrollSpecs, impl.interruptionHandler, + impl.defaultOverscrollProgressConverter, ) } private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { override var defaultSwipeSpec: SpringSpec<Float> = SceneTransitions.DefaultSwipeSpec override var interruptionHandler: InterruptionHandler = DefaultInterruptionHandler + override var defaultOverscrollProgressConverter: ProgressConverter = ProgressConverter.Default val transitionSpecs = mutableListOf<TransitionSpecImpl>() val transitionOverscrollSpecs = mutableListOf<OverscrollSpecImpl>() @@ -271,7 +273,7 @@ internal class TransitionBuilderImpl : BaseTransitionBuilderImpl(), TransitionBu } internal open class OverscrollBuilderImpl : BaseTransitionBuilderImpl(), OverscrollBuilder { - override var progressConverter: ProgressConverter = ProgressConverter.Default + override var progressConverter: ProgressConverter? = null override fun translate( matcher: ElementMatcher, diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt index 34e609095e90..20b9b49c21d7 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt @@ -961,6 +961,97 @@ class ElementTest { } @Test + fun elementTransitionWithDistanceDuringOverscrollWithDefaultProgressConverter() { + val layoutWidth = 200.dp + val layoutHeight = 400.dp + var animatedFloat = 0f + val state = + setupOverscrollScenario( + layoutWidth = layoutWidth, + layoutHeight = layoutHeight, + sceneTransitions = { + // Overscroll progress will be halved + defaultOverscrollProgressConverter = ProgressConverter { it / 2f } + + overscroll(SceneB, Orientation.Vertical) { + // On overscroll 100% -> Foo should translate by layoutHeight + translate(TestElements.Foo, y = { absoluteDistance }) + } + }, + firstScroll = 1f, // 100% scroll + animatedFloatRange = 0f..100f, + onAnimatedFloat = { animatedFloat = it }, + ) + + val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag) + fooElement.assertTopPositionInRootIsEqualTo(0.dp) + assertThat(animatedFloat).isEqualTo(100f) + + rule.onRoot().performTouchInput { + // Scroll another 100% + moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000) + } + + val transition = assertThat(state.transitionState).isTransition() + assertThat(animatedFloat).isEqualTo(100f) + + // Scroll 200% (100% scroll + 100% overscroll) + assertThat(transition).hasProgress(2f) + assertThat(transition).hasOverscrollSpec() + + // Overscroll progress is halved, we are at 50% of the overscroll progress. + fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * 0.5f) + assertThat(animatedFloat).isEqualTo(100f) + } + + @Test + fun elementTransitionWithDistanceDuringOverscrollWithOverrideDefaultProgressConverter() { + val layoutWidth = 200.dp + val layoutHeight = 400.dp + var animatedFloat = 0f + val state = + setupOverscrollScenario( + layoutWidth = layoutWidth, + layoutHeight = layoutHeight, + sceneTransitions = { + // Overscroll progress will be linear (by default) + defaultOverscrollProgressConverter = ProgressConverter { it } + + overscroll(SceneB, Orientation.Vertical) { + // This override the defaultOverscrollProgressConverter + // Overscroll progress will be halved + progressConverter = ProgressConverter { it / 2f } + // On overscroll 100% -> Foo should translate by layoutHeight + translate(TestElements.Foo, y = { absoluteDistance }) + } + }, + firstScroll = 1f, // 100% scroll + animatedFloatRange = 0f..100f, + onAnimatedFloat = { animatedFloat = it }, + ) + + val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag) + fooElement.assertTopPositionInRootIsEqualTo(0.dp) + assertThat(animatedFloat).isEqualTo(100f) + + rule.onRoot().performTouchInput { + // Scroll another 100% + moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000) + } + + val transition = assertThat(state.transitionState).isTransition() + assertThat(animatedFloat).isEqualTo(100f) + + // Scroll 200% (100% scroll + 100% overscroll) + assertThat(transition).hasProgress(2f) + assertThat(transition).hasOverscrollSpec() + + // Overscroll progress is halved, we are at 50% of the overscroll progress. + fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * 0.5f) + assertThat(animatedFloat).isEqualTo(100f) + } + + @Test fun elementTransitionWithDistanceDuringOverscrollWithProgressConverter() { val layoutWidth = 200.dp val layoutHeight = 400.dp |