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

Commit 55b22b68 authored by omarmt's avatar omarmt Committed by Omar Miatello
Browse files

Handle gesture started on nested child in SceneTransitionLayout

When you start scrolling one of its children, this mode is activated.
It prevents the rest of the gesture from being used at the end of the
child's scroll
 to pass (accidentally) to the next scene.

Instead, the rest of the gesture will only be used partially to show a
preview of the next scene.

Test: mp SystemUIComposeGallery
Bug: 291025415
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:599ce55847dcd7c8442ee90efc2f8eef2190497d)
Change-Id: Ifaa3b26fbe3d5ef5262a23fa0d3a556d8353321a
parent d8446a66
Loading
Loading
Loading
Loading
+23 −10
Original line number Diff line number Diff line
@@ -327,6 +327,7 @@ private fun CoroutineScope.onDragStopped(
    velocity: Float,
    velocityThreshold: Float,
    positionalThreshold: Float,
    canChangeScene: Boolean = true,
) {
    // The state was changed since the drag started; don't do anything.
    if (layoutImpl.state.transitionState != transition) {
@@ -345,6 +346,7 @@ private fun CoroutineScope.onDragStopped(
    val offset = transition.dragOffset
    val distance = transition.distance
    if (
        canChangeScene &&
            shouldCommitSwipe(
                offset,
                distance,
@@ -450,6 +452,7 @@ private fun rememberSwipeToSceneNestedScrollConnection(
    velocityThreshold: Float,
    positionalThreshold: Float,
): PriorityPostNestedScrollConnection {
    val density = LocalDensity.current
    val scrollConnection =
        remember(
            orientation,
@@ -459,6 +462,7 @@ private fun rememberSwipeToSceneNestedScrollConnection(
            layoutImpl,
            velocityThreshold,
            positionalThreshold,
            density,
        ) {
            fun Offset.toAmount() =
                when (orientation) {
@@ -484,11 +488,17 @@ private fun rememberSwipeToSceneNestedScrollConnection(
            // This is the scene on which we will have priority during the scroll gesture.
            var priorityScene: SceneKey? = null

            // If we performed a long gesture before entering priority mode, we would have to avoid
            // moving on to the next scene.
            var gestureStartedOnNestedChild = false

            PriorityPostNestedScrollConnection(
                canStart = { offsetAvailable ->
                canStart = { offsetAvailable, offsetBeforeStart ->
                    val amount = offsetAvailable.toAmount()
                    if (amount == 0f) return@PriorityPostNestedScrollConnection false

                    gestureStartedOnNestedChild = offsetBeforeStart != Offset.Zero

                    val fromScene = layoutImpl.scene(layoutImpl.state.transitionState.currentScene)
                    nextScene =
                        when {
@@ -502,12 +512,14 @@ private fun rememberSwipeToSceneNestedScrollConnection(
                canContinueScroll = { priorityScene == transition._toScene.key },
                onStart = {
                    priorityScene = nextScene

                    onDragStarted(layoutImpl, transition, orientation)
                },
                onScroll = { offsetAvailable ->
                    val amount = offsetAvailable.toAmount()

                    // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture
                    // is initiated in a nested child.

                    // Appends a new coroutine to attempt to drag by [amount] px. In this case we
                    // are assuming that the [coroutineScope] is tied to the main thread and that
                    // calls to [launch] are therefore queued.
@@ -524,6 +536,7 @@ private fun rememberSwipeToSceneNestedScrollConnection(
                        velocity = velocityAvailable.toAmount(),
                        velocityThreshold = velocityThreshold,
                        positionalThreshold = positionalThreshold,
                        canChangeScene = !gestureStartedOnNestedChild
                    )

                    // The onDragStopped animation consumes any remaining velocity.
+21 −2
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ import androidx.compose.ui.unit.Velocity
 * @sample com.android.compose.animation.scene.rememberSwipeToSceneNestedScrollConnection
 */
class PriorityPostNestedScrollConnection(
    private val canStart: (offsetAvailable: Offset) -> Boolean,
    private val canStart: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
    private val canContinueScroll: () -> Boolean,
    private val onStart: () -> Unit,
    private val onScroll: (offsetAvailable: Offset) -> Offset,
@@ -43,12 +43,22 @@ class PriorityPostNestedScrollConnection(
    /** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */
    private var isPriorityMode = false

    private var offsetScrolledBeforePriorityMode = Offset.Zero

    override fun onPostScroll(
        consumed: Offset,
        available: Offset,
        source: NestedScrollSource,
    ): Offset {
        if (isPriorityMode || source == NestedScrollSource.Fling || !canStart(available)) {
        // The offset before the start takes into account the up and down movements, starting from
        // the beginning or from the last fling gesture.
        val offsetBeforeStart = offsetScrolledBeforePriorityMode - available

        if (
            isPriorityMode ||
                source == NestedScrollSource.Fling ||
                !canStart(available, offsetBeforeStart)
        ) {
            // The priority mode cannot start so we won't consume the available offset.
            return Offset.Zero
        }
@@ -66,6 +76,11 @@ class PriorityPostNestedScrollConnection(

    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
        if (!isPriorityMode) {
            if (source != NestedScrollSource.Fling) {
                // We want to track the amount of offset consumed before entering priority mode
                offsetScrolledBeforePriorityMode += available
            }

            return Offset.Zero
        }

@@ -96,6 +111,10 @@ class PriorityPostNestedScrollConnection(
    }

    private fun onPriorityStop(velocity: Velocity): Velocity {

        // We can restart tracking the consumed offsets from scratch.
        offsetScrolledBeforePriorityMode = Offset.Zero

        if (!isPriorityMode) {
            return Velocity.Zero
        }
+1 −1
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ class PriorityPostNestedScrollConnectionTest {

    private val scrollConnection =
        PriorityPostNestedScrollConnection(
            canStart = { canStart },
            canStart = { _, _ -> canStart },
            canContinueScroll = { canContinueScroll },
            onStart = { isStarted = true },
            onScroll = {