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

Commit 08a4f97f authored by omarmt's avatar omarmt
Browse files

PriorityNestedScrollConnection simplification: remove onPostFling method

To simplify the logic for acquiring priority during postFling, we've
added a canStartPostFling method. If this method returns true, it will
call onStart and then immediately onStop (since the last event was
launched during a nested scroll gesture).

Test: atest PriorityNestedScrollConnectionTest
Bug: 291025415
Change-Id: I7ade358f2097a15d1328f3e6706df0ec12d23acf
parent b51f7231
Loading
Loading
Loading
Loading
+18 −20
Original line number Diff line number Diff line
@@ -556,6 +556,15 @@ class SceneNestedScrollHandler(
        // moving on to the next scene.
        var gestureStartedOnNestedChild = false

        fun findNextScene(amount: Float): SceneKey? {
            val fromScene = gestureHandler.currentScene
            return when {
                amount < 0f -> fromScene.upOrLeft(gestureHandler.orientation)
                amount > 0f -> fromScene.downOrRight(gestureHandler.orientation)
                else -> null
            }
        }

        return PriorityNestedScrollConnection(
            canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
                gestureStartedOnNestedChild = offsetBeforeStart != Offset.Zero
@@ -576,15 +585,16 @@ class SceneNestedScrollHandler(
                if (amount == 0f) return@PriorityNestedScrollConnection false

                gestureStartedOnNestedChild = offsetBeforeStart != Offset.Zero
                nextScene = findNextScene(amount)
                nextScene != null
            },
            canStartPostFling = { velocityAvailable ->
                val amount = velocityAvailable.toAmount()
                if (amount == 0f) return@PriorityNestedScrollConnection false

                val fromScene = gestureHandler.currentScene
                nextScene =
                    when {
                        amount < 0f -> fromScene.upOrLeft(gestureHandler.orientation)
                        amount > 0f -> fromScene.downOrRight(gestureHandler.orientation)
                        else -> null
                    }

                // We could start an overscroll animation
                gestureStartedOnNestedChild = true
                nextScene = findNextScene(amount)
                nextScene != null
            },
            canContinueScroll = { priorityScene == gestureHandler.swipeTransitionToScene.key },
@@ -612,18 +622,6 @@ class SceneNestedScrollHandler(
                // The onDragStopped animation consumes any remaining velocity.
                velocityAvailable
            },
            onPostFling = { velocityAvailable ->
                val velocityAmount = velocityAvailable.toAmount()
                if (velocityAmount != 0f) {
                    // If there is any velocity left, we can try running an overscroll animation
                    // between scenes.
                    gestureHandler.onDragStarted()
                    gestureHandler.onDragStopped(velocity = velocityAmount, canChangeScene = false)
                }

                // We consumed any remaining velocity.
                velocityAvailable
            },
        )
    }
}
+9 −2
Original line number Diff line number Diff line
@@ -34,11 +34,11 @@ import androidx.compose.ui.unit.Velocity
class PriorityNestedScrollConnection(
    private val canStartPreScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
    private val canStartPostScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
    private val canStartPostFling: (velocityAvailable: Velocity) -> Boolean,
    private val canContinueScroll: () -> Boolean,
    private val onStart: () -> Unit,
    private val onScroll: (offsetAvailable: Offset) -> Offset,
    private val onStop: (velocityAvailable: Velocity) -> Velocity,
    private val onPostFling: suspend (velocityAvailable: Velocity) -> Velocity,
) : NestedScrollConnection {

    /** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */
@@ -102,7 +102,14 @@ class PriorityNestedScrollConnection(
    }

    override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
        return onPostFling(available)
        if (!canStartPostFling(available)) {
            return Velocity.Zero
        }

        onPriorityStart(available = Offset.Zero)

        // This is the last event of a scroll gesture.
        return onPriorityStop(available)
    }

    /** Method to call before destroying the object or to reset the initial state. */
+26 −7
Original line number Diff line number Diff line
@@ -32,19 +32,19 @@ import org.junit.runner.RunWith
class PriorityNestedScrollConnectionTest {
    private var canStartPreScroll = false
    private var canStartPostScroll = false
    private var canStartPostFling = false
    private var canContinueScroll = false
    private var isStarted = false
    private var lastScroll: Offset? = null
    private var returnOnScroll = Offset.Zero
    private var lastStop: Velocity? = null
    private var returnOnStop = Velocity.Zero
    private var lastOnPostFling: Velocity? = null
    private var returnOnPostFling = Velocity.Zero

    private val scrollConnection =
        PriorityNestedScrollConnection(
            canStartPreScroll = { _, _ -> canStartPreScroll },
            canStartPostScroll = { _, _ -> canStartPostScroll },
            canStartPostFling = { canStartPostFling },
            canContinueScroll = { canContinueScroll },
            onStart = { isStarted = true },
            onScroll = {
@@ -55,10 +55,6 @@ class PriorityNestedScrollConnectionTest {
                lastStop = it
                returnOnStop
            },
            onPostFling = {
                lastOnPostFling = it
                returnOnPostFling
            },
        )

    private val offset1 = Offset(1f, 1f)
@@ -185,11 +181,34 @@ class PriorityNestedScrollConnectionTest {

    @Test
    fun receive_onPostFling() = runTest {
        canStartPostFling = true

        scrollConnection.onPostFling(
            consumed = velocity1,
            available = velocity2,
        )

        assertThat(lastOnPostFling).isEqualTo(velocity2)
        assertThat(lastStop).isEqualTo(velocity2)
    }

    @Test
    fun step1_priorityModeShouldStartOnlyOnPostFling() = runTest {
        canStartPostFling = true

        scrollConnection.onPreScroll(available = Offset.Zero, source = NestedScrollSource.Drag)
        assertThat(isStarted).isEqualTo(false)

        scrollConnection.onPostScroll(
            consumed = Offset.Zero,
            available = Offset.Zero,
            source = NestedScrollSource.Drag
        )
        assertThat(isStarted).isEqualTo(false)

        scrollConnection.onPreFling(available = Velocity.Zero)
        assertThat(isStarted).isEqualTo(false)

        scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero)
        assertThat(isStarted).isEqualTo(true)
    }
}