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

Commit b51f7231 authored by omarmt's avatar omarmt
Browse files

SceneGestureHandler simplification: remove overscroll method

In order to simplify the reasoning about the states of the transition
between scenes, it is possible to remove the overscroll animation, since
 it is conceptually equivalent to calling the stop method without
 permission to change scene.

Note about the test: we changed the overscroll implementation and the
overscroll test (scrollStartedInScene_doOverscrollAnimation) is still
working.

Test: atest SceneGestureHandlerTest
Bug: 291025415
Change-Id: Id0ef8ab9d7855452a3d6c8f3581064d80f32c14a
parent e73b0cb0
Loading
Loading
Loading
Loading
+74 −77
Original line number Diff line number Diff line
@@ -196,6 +196,8 @@ class SceneGestureHandler(
    }

    internal fun onDrag(delta: Float) {
        if (delta == 0f) return

        swipeTransition.dragOffset += delta

        // First check transition.fromScene should be changed for the case where the user quickly
@@ -293,48 +295,77 @@ class SceneGestureHandler(
            return
        }

        fun animateTo(targetScene: Scene, targetOffset: Float) {
            // If the effective current scene changed, it should be reflected right now in the
            // current scene state, even before the settle animation is ongoing. That way all the
            // swipeables and back handlers will be refreshed and the user can for instance quickly
            // swipe vertically from A => B then horizontally from B => C, or swipe from A => B then
            // immediately go back B => A.
            if (targetScene != swipeTransition._currentScene) {
                swipeTransition._currentScene = targetScene
                layoutImpl.onChangeScene(targetScene.key)
            }

            animateOffset(
                initialVelocity = velocity,
                targetOffset = targetOffset,
                targetScene = targetScene.key
            )
        }

        val fromScene = swipeTransition._fromScene
        if (canChangeScene) {
            // If we are halfway between two scenes, we check what the target will be based on the
            // velocity and offset of the transition, then we launch the animation.

            val toScene = swipeTransition._toScene
            if (fromScene == toScene) {
                // We were not animating.
        if (swipeTransition._fromScene == swipeTransition._toScene) {
            transitionState = TransitionState.Idle(swipeTransition._fromScene.key)
                transitionState = TransitionState.Idle(fromScene.key)
                return
            }

            // Compute the destination scene (and therefore offset) to settle in.
        val targetOffset: Float
        val targetScene: Scene
            val offset = swipeTransition.dragOffset
            val distance = swipeTransition.distance
            if (
            canChangeScene &&
                shouldCommitSwipe(
                    offset,
                    distance,
                    velocity,
                    wasCommitted = swipeTransition._currentScene == swipeTransition._toScene,
                    wasCommitted = swipeTransition._currentScene == toScene,
                )
            ) {
            targetOffset = distance
            targetScene = swipeTransition._toScene
                // Animate to the next scene
                animateTo(targetScene = toScene, targetOffset = distance)
            } else {
            targetOffset = 0f
            targetScene = swipeTransition._fromScene
                // Animate to the initial scene
                animateTo(targetScene = fromScene, targetOffset = 0f)
            }
        } else {
            // We are doing an overscroll animation between scenes. In this case, we can also start
            // from the idle position.

        // If the effective current scene changed, it should be reflected right now in the current
        // scene state, even before the settle animation is ongoing. That way all the swipeables and
        // back handlers will be refreshed and the user can for instance quickly swipe vertically
        // from A => B then horizontally from B => C, or swipe from A => B then immediately go back
        // B => A.
        if (targetScene != swipeTransition._currentScene) {
            swipeTransition._currentScene = targetScene
            layoutImpl.onChangeScene(targetScene.key)
        }
            val startFromIdlePosition = swipeTransition.dragOffset == 0f

        animateOffset(
            initialVelocity = velocity,
            targetOffset = targetOffset,
            targetScene = targetScene.key
        )
            if (startFromIdlePosition) {
                // If there is a next scene, we start the overscroll animation.
                val target = fromScene.findTargetSceneAndDistance(velocity)
                val isValidTarget = target.distance != 0f && target.sceneKey != fromScene.key
                if (isValidTarget) {
                    swipeTransition._toScene = layoutImpl.scene(target.sceneKey)
                    swipeTransition._distance = target.distance

                    animateTo(targetScene = fromScene, targetOffset = 0f)
                } else {
                    // We will not animate
                    transitionState = TransitionState.Idle(fromScene.key)
                }
            } else {
                // We were between two scenes: animate to the initial scene.
                animateTo(targetScene = fromScene, targetOffset = 0f)
            }
        }
    }

    /**
@@ -407,47 +438,6 @@ class SceneGestureHandler(
        }
    }

    internal fun animateOverscroll(velocity: Velocity): Velocity {
        val velocityAmount =
            when (orientation) {
                Orientation.Vertical -> velocity.y
                Orientation.Horizontal -> velocity.x
            }

        if (velocityAmount == 0f) {
            // There is no remaining velocity
            return Velocity.Zero
        }

        val fromScene = currentScene
        val target = fromScene.findTargetSceneAndDistance(velocityAmount)
        val isValidTarget = target.distance != 0f && target.sceneKey != fromScene.key

        if (!isValidTarget || isDrivingTransition) {
            // We have not found a valid target or we are already in a transition
            return Velocity.Zero
        }

        swipeTransition._currentScene = fromScene
        swipeTransition._fromScene = fromScene
        swipeTransition._toScene = layoutImpl.scene(target.sceneKey)
        swipeTransition._distance = target.distance
        swipeTransition.absoluteDistance = target.distance.absoluteValue
        swipeTransition.stopOffsetAnimation()
        swipeTransition.dragOffset = 0f

        transitionState = swipeTransition

        animateOffset(
            initialVelocity = velocityAmount,
            targetOffset = 0f,
            targetScene = fromScene.key
        )

        // The animateOffset animation consumes any remaining velocity.
        return velocity
    }

    private class SwipeTransition(initialScene: Scene) : TransitionState.Transition {
        var _currentScene by mutableStateOf(initialScene)
        override val currentScene: SceneKey
@@ -623,9 +613,16 @@ class SceneNestedScrollHandler(
                velocityAvailable
            },
            onPostFling = { velocityAvailable ->
                // If there is any velocity left, we can try running an overscroll animation between
                // scenes.
                gestureHandler.animateOverscroll(velocity = 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
            },
        )
    }