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

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

Merge "Add progressConverter property to OverscrollBuilder (1/2)" into main

parents 5eb72139 524c5b72
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1088,8 +1088,8 @@ private inline fun <T> computeValue(
            // range.
            val directionSign = if (transition.isUpOrLeft) -1 else 1
            val isToScene = overscroll.scene == transition.toScene
            val overscrollProgress = transition.progress.let { if (isToScene) it - 1f else it }
            val progress = directionSign * overscrollProgress
            val linearProgress = transition.progress.let { if (isToScene) it - 1f else it }
            val progress = directionSign * overscroll.progressConverter(linearProgress)
            val rangeProgress = propertySpec.range?.progress(progress) ?: progress

            // Interpolate between the value at rest and the over scrolled value.
+15 −3
Original line number Diff line number Diff line
@@ -48,7 +48,8 @@ internal constructor(
) {
    private val transitionCache =
        mutableMapOf<
            SceneKey, MutableMap<SceneKey, MutableMap<TransitionKey?, TransitionSpecImpl>>
            SceneKey,
            MutableMap<SceneKey, MutableMap<TransitionKey?, TransitionSpecImpl>>
        >()

    private val overscrollCache =
@@ -87,8 +88,7 @@ internal constructor(
        return transition(from, to, key) {
                (it.from == to && it.to == null) || (it.to == from && it.from == null)
            }
            ?.reversed()
            ?: defaultTransition(from, to)
            ?.reversed() ?: defaultTransition(from, to)
    }

    private fun transition(
@@ -257,12 +257,24 @@ interface OverscrollSpec {

    /** The [TransformationSpec] associated to this [OverscrollSpec]. */
    val transformationSpec: TransformationSpec

    /**
     * Function that takes a linear overscroll progress value ranging from 0 to +/- infinity and
     * outputs the desired **overscroll progress value**.
     *
     * When the progress value is:
     * - 0, the user is not overscrolling.
     * - 1, the user overscrolled by exactly the [OverscrollBuilder.distance].
     * - Greater than 1, the user overscrolled more than the [OverscrollBuilder.distance].
     */
    val progressConverter: (Float) -> Float
}

internal class OverscrollSpecImpl(
    override val scene: SceneKey,
    override val orientation: Orientation,
    override val transformationSpec: TransformationSpecImpl,
    override val progressConverter: (Float) -> Float,
) : OverscrollSpec

/**
+11 −0
Original line number Diff line number Diff line
@@ -185,6 +185,17 @@ interface TransitionBuilder : BaseTransitionBuilder {

@TransitionDsl
interface OverscrollBuilder : BaseTransitionBuilder {
    /**
     * Function that takes a linear overscroll progress value ranging from 0 to +/- infinity and
     * outputs the desired **overscroll progress value**.
     *
     * When the progress value is:
     * - 0, the user is not overscrolling.
     * - 1, the user overscrolled by exactly the [distance].
     * - Greater than 1, the user overscrolled more than the [distance].
     */
    var progressConverter: (Float) -> Float

    /** Translate the element(s) matching [matcher] by ([x], [y]) pixels. */
    fun translate(
        matcher: ElementMatcher,
+15 −9
Original line number Diff line number Diff line
@@ -81,16 +81,20 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder {
        orientation: Orientation,
        builder: OverscrollBuilder.() -> Unit
    ): OverscrollSpec {
        fun transformationSpec(): TransformationSpecImpl {
        val impl = OverscrollBuilderImpl().apply(builder)
            return TransformationSpecImpl(
        val spec =
            OverscrollSpecImpl(
                scene = scene,
                orientation = orientation,
                transformationSpec =
                    TransformationSpecImpl(
                        progressSpec = snap(),
                        swipeSpec = null,
                        distance = impl.distance,
                        transformations = impl.transformations,
                    ),
                progressConverter = impl.progressConverter
            )
        }
        val spec = OverscrollSpecImpl(scene, orientation, transformationSpec())
        transitionOverscrollSpecs.add(spec)
        return spec
    }
@@ -231,6 +235,8 @@ internal class TransitionBuilderImpl : BaseTransitionBuilderImpl(), TransitionBu
}

internal open class OverscrollBuilderImpl : BaseTransitionBuilderImpl(), OverscrollBuilder {
    override var progressConverter: (Float) -> Float = { it }

    override fun translate(
        matcher: ElementMatcher,
        x: OverscrollScope.() -> Float,
+57 −0
Original line number Diff line number Diff line
@@ -888,6 +888,63 @@ class ElementTest {
        assertThat(animatedFloat).isEqualTo(100f)
    }

    @Test
    fun elementTransitionWithDistanceDuringOverscrollWithProgressConverter() {
        val layoutWidth = 200.dp
        val layoutHeight = 400.dp
        var animatedFloat = 0f
        val state =
            setupOverscrollScenario(
                layoutWidth = layoutWidth,
                layoutHeight = layoutHeight,
                sceneTransitions = {
                    overscroll(SceneB, Orientation.Vertical) {
                        // Overscroll progress will be halved
                        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)

        rule.onRoot().performTouchInput {
            // Scroll another 100%
            moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
        }

        // Scroll 300% (100% scroll + 200% overscroll)
        assertThat(transition).hasProgress(3f)
        assertThat(transition).hasOverscrollSpec()

        // Overscroll progress is halved, we are at 100% of the overscroll progress.
        fooElement.assertTopPositionInRootIsEqualTo(layoutHeight)
        assertThat(animatedFloat).isEqualTo(100f)
    }

    @Test
    fun elementTransitionWithDistanceDuringOverscrollBouncing() {
        val layoutWidth = 200.dp