Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9170c939 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "STL: Add defaultOverscrollProgressConverter in SceneTransitions [1/2]" into main

parents 6fd37ffb 2307093d
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -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.
+4 −2
Original line number Diff line number Diff line
@@ -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

/**
+7 −1
Original line number Diff line number Diff line
@@ -49,6 +49,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
@@ -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(
+3 −1
Original line number Diff line number Diff line
@@ -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,
+91 −0
Original line number Diff line number Diff line
@@ -960,6 +960,97 @@ class ElementTest {
        assertThat(animatedFloat).isEqualTo(100f)
    }

    @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