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

Commit f780c2f0 authored by Omar Miatello's avatar Omar Miatello Committed by Android (Google) Code Review
Browse files

Merge "Add snapToIdleIfClose in BaseSceneTransitionLayoutState" into main

parents 62c2e3f7 cff3bc53
Loading
Loading
Loading
Loading
+10 −18
Original line number Diff line number Diff line
@@ -701,27 +701,19 @@ internal class SceneNestedScrollHandler(
                        gestureHandler.shouldImmediatelyIntercept(startedPosition = null)
                if (!canInterceptSwipeTransition) return@PriorityNestedScrollConnection false

                val swipeTransition = gestureHandler.swipeTransition
                val progress = swipeTransition.progress
                val threshold = layoutImpl.transitionInterceptionThreshold
                fun isProgressCloseTo(value: Float) = (progress - value).absoluteValue <= threshold

                // The transition is always between 0 and 1. If it is close to either of these
                // intervals, we want to go directly to the TransitionState.Idle.
                // The progress value can go beyond this range in the case of overscroll.
                val shouldSnapToIdle = isProgressCloseTo(0f) || isProgressCloseTo(1f)
                if (shouldSnapToIdle) {
                    swipeTransition.cancelOffsetAnimation()
                    layoutState.finishTransition(swipeTransition, swipeTransition.currentScene)
                val hasSnappedToIdle = layoutState.snapToIdleIfClose(threshold)
                if (hasSnappedToIdle) {
                    // If the current swipe transition is closed to 0f or 1f, then we want to
                    // interrupt the transition (snapping it to Idle) and scroll the list.
                    return@PriorityNestedScrollConnection false
                }

                // Start only if we cannot consume this event
                val canStart = !shouldSnapToIdle
                if (canStart) {
                // If the current swipe transition is *not* closed to 0f or 1f, then we want the
                // scroll events to intercept the current transition to continue the scene
                // transition.
                isIntercepting = true
                }

                canStart
                true
            },
            canStartPostScroll = { offsetAvailable, offsetBeforeStart ->
                val behavior: NestedScrollBehavior =
+25 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel

@@ -236,6 +237,30 @@ internal abstract class BaseSceneTransitionLayoutState(initialScene: SceneKey) :
            transitionState = TransitionState.Idle(idleScene)
        }
    }

    /**
     * Check if a transition is in progress. If the progress value is near 0 or 1, immediately snap
     * to the closest scene.
     *
     * @return true if snapped to the closest scene.
     */
    internal fun snapToIdleIfClose(threshold: Float): Boolean {
        val transition = currentTransition ?: return false
        val progress = transition.progress
        fun isProgressCloseTo(value: Float) = (progress - value).absoluteValue <= threshold

        return when {
            isProgressCloseTo(0f) -> {
                finishTransition(transition, transition.fromScene)
                true
            }
            isProgressCloseTo(1f) -> {
                finishTransition(transition, transition.toScene)
                true
            }
            else -> false
        }
    }
}

/**
+38 −0
Original line number Diff line number Diff line
@@ -158,4 +158,42 @@ class SceneTransitionLayoutStateTest {
            .isNotNull()
        assertThat(state.transformationSpec.transformations).hasSize(2)
    }

    @Test
    fun snapToIdleIfClose_snapToStart() = runMonotonicClockTest {
        val state = MutableSceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty)
        state.startTransition(
            transition(from = TestScenes.SceneA, to = TestScenes.SceneB, progress = { 0.2f }),
            transitionKey = null
        )
        assertThat(state.isTransitioning()).isTrue()

        // Ignore the request if the progress is not close to 0 or 1, using the threshold.
        assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
        assertThat(state.isTransitioning()).isTrue()

        // Go to the initial scene if it is close to 0.
        assertThat(state.snapToIdleIfClose(threshold = 0.2f)).isTrue()
        assertThat(state.isTransitioning()).isFalse()
        assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneA))
    }

    @Test
    fun snapToIdleIfClose_snapToEnd() = runMonotonicClockTest {
        val state = MutableSceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty)
        state.startTransition(
            transition(from = TestScenes.SceneA, to = TestScenes.SceneB, progress = { 0.8f }),
            transitionKey = null
        )
        assertThat(state.isTransitioning()).isTrue()

        // Ignore the request if the progress is not close to 0 or 1, using the threshold.
        assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
        assertThat(state.isTransitioning()).isTrue()

        // Go to the final scene if it is close to 1.
        assertThat(state.snapToIdleIfClose(threshold = 0.2f)).isTrue()
        assertThat(state.isTransitioning()).isFalse()
        assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB))
    }
}