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

Commit 26ec7328 authored by omarmt's avatar omarmt
Browse files

Refactor Swipes

Moved the methods that affect the Swipes object into the same object so
that it is easier to manage the destinations.

Test: No new tests
Bug: 317063114
Flag: NA
Change-Id: Ib0dde168e8d76589a803d558fac218e2ea4680c5
parent 1d967cdc
Loading
Loading
Loading
Loading
+107 −105
Original line number Diff line number Diff line
@@ -92,10 +92,6 @@ internal class SceneGestureHandler(
    /** The [Swipes] associated to the current gesture. */
    private var swipes: Swipes? = null

    /** The [UserActionResult] associated to up and down swipes. */
    private var upOrLeftResult: UserActionResult? = null
    private var downOrRightResult: UserActionResult? = null

    /**
     * Whether we should immediately intercept a gesture.
     *
@@ -128,7 +124,7 @@ internal class SceneGestureHandler(
            // This [transition] was already driving the animation: simply take over it.
            // Stop animating and start from where the current offset.
            swipeTransition.cancelOffsetAnimation()
            updateSwipesResults(swipeTransition._fromScene)
            swipes!!.updateSwipesResults(swipeTransition._fromScene)
            return
        }

@@ -144,17 +140,19 @@ internal class SceneGestureHandler(
        }

        val fromScene = layoutImpl.scene(transitionState.currentScene)
        updateSwipes(fromScene, startedPosition, pointersDown)
        val newSwipes = computeSwipes(fromScene, startedPosition, pointersDown)
        swipes = newSwipes
        val result = newSwipes.findUserActionResult(fromScene, overSlop, true)

        // As we were unable to locate a valid target scene, the initial SwipeTransition cannot be
        // defined.
        if (result == null) return

        val result =
            findUserActionResult(fromScene, directionOffset = overSlop, updateSwipesResults = true)
                ?: return
        val newSwipeTransition =
            SwipeTransition(
                fromScene = fromScene,
                result = result,
                upOrLeftResult = upOrLeftResult,
                downOrRightResult = downOrRightResult,
                swipes = newSwipes,
                layoutImpl = layoutImpl,
                orientation = orientation
            )
@@ -162,10 +160,6 @@ internal class SceneGestureHandler(
        updateTransition(newSwipeTransition, force = true)
    }

    private fun updateSwipes(fromScene: Scene, startedPosition: Offset?, pointersDown: Int) {
        this.swipes = computeSwipes(fromScene, startedPosition, pointersDown)
    }

    private fun computeSwipes(
        fromScene: Scene,
        startedPosition: Offset?,
@@ -220,13 +214,6 @@ internal class SceneGestureHandler(
        }
    }

    private fun Scene.getAbsoluteDistance(distance: UserActionDistance?): Float {
        val targetSize = this.targetSize
        return with(distance ?: DefaultSwipeDistance) {
            layoutImpl.density.absoluteDistance(targetSize, orientation)
        }
    }

    internal fun onDrag(delta: Float) {
        if (delta == 0f || !isDrivingTransition) return
        swipeTransition.dragOffset += delta
@@ -236,15 +223,17 @@ internal class SceneGestureHandler(

        val isNewFromScene = fromScene.key != swipeTransition.fromScene
        val result =
            findUserActionResult(
                fromScene,
                swipeTransition.dragOffset,
                updateSwipesResults = isNewFromScene,
            swipes!!.findUserActionResult(
                fromScene = fromScene,
                directionOffset = swipeTransition.dragOffset,
                updateSwipesResults = isNewFromScene
            )
                ?: run {
                    onDragStopped(delta, true)

        if (result == null) {
            onDragStopped(velocity = delta, canChangeScene = true)
            return
        }

        swipeTransition.dragOffset += acceleratedOffset

        if (
@@ -256,8 +245,7 @@ internal class SceneGestureHandler(
                SwipeTransition(
                        fromScene = fromScene,
                        result = result,
                        upOrLeftResult = upOrLeftResult,
                        downOrRightResult = downOrRightResult,
                        swipes = swipes!!,
                        layoutImpl = layoutImpl,
                        orientation = orientation
                    )
@@ -267,17 +255,6 @@ internal class SceneGestureHandler(
        }
    }

    private fun updateSwipesResults(fromScene: Scene) {
        val (upOrLeftResult, downOrRightResult) =
            computeSwipesResults(
                fromScene,
                this.swipes ?: error("updateSwipes() should be called before updateSwipesResults()")
            )

        this.upOrLeftResult = upOrLeftResult
        this.downOrRightResult = downOrRightResult
    }

    private fun computeSwipesResults(
        fromScene: Scene,
        swipes: Swipes
@@ -312,62 +289,20 @@ internal class SceneGestureHandler(

        // If the swipe was not committed, don't do anything.
        if (swipeTransition._currentScene != toScene) {
            return Pair(fromScene, 0f)
            return fromScene to 0f
        }

        // If the offset is past the distance then let's change fromScene so that the user can swipe
        // to the next screen or go back to the previous one.
        val offset = swipeTransition.dragOffset
        return if (offset <= -absoluteDistance && upOrLeftResult?.toScene == toScene.key) {
            Pair(toScene, absoluteDistance)
        } else if (offset >= absoluteDistance && downOrRightResult?.toScene == toScene.key) {
            Pair(toScene, -absoluteDistance)
        return if (offset <= -absoluteDistance && swipes!!.upOrLeftResult?.toScene == toScene.key) {
            toScene to absoluteDistance
        } else if (
            offset >= absoluteDistance && swipes!!.downOrRightResult?.toScene == toScene.key
        ) {
            toScene to -absoluteDistance
        } else {
            Pair(fromScene, 0f)
        }
    }

    /**
     * Returns the [UserActionResult] from [fromScene] in the direction of [directionOffset].
     *
     * @param fromScene the scene from which we look for the target
     * @param directionOffset signed float that indicates the direction. Positive is down or right
     *   negative is up or left.
     * @param updateSwipesResults whether the target scenes should be updated to the current values
     *   held in the Scenes map. Usually we don't want to update them while doing a drag, because
     *   this could change the target scene (jump cutting) to a different scene, when some system
     *   state changed the targets the background. However, an update is needed any time we
     *   calculate the targets for a new fromScene.
     * @return null when there are no targets in either direction. If one direction is null and you
     *   drag into the null direction this function will return the opposite direction, assuming
     *   that the users intention is to start the drag into the other direction eventually. If
     *   [directionOffset] is 0f and both direction are available, it will default to
     *   [upOrLeftResult].
     */
    private fun findUserActionResult(
        fromScene: Scene,
        directionOffset: Float,
        updateSwipesResults: Boolean,
    ): UserActionResult? {
        if (updateSwipesResults) updateSwipesResults(fromScene)

        return when {
            upOrLeftResult == null && downOrRightResult == null -> null
            (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null ->
                upOrLeftResult
            else -> downOrRightResult
        }
    }

    /**
     * A strict version of [findUserActionResult] that will return null when there is no Scene in
     * [directionOffset] direction
     */
    private fun findUserActionResultStrict(directionOffset: Float): UserActionResult? {
        return when {
            directionOffset > 0f -> upOrLeftResult
            directionOffset < 0f -> downOrRightResult
            else -> null
            fromScene to 0f
        }
    }

@@ -435,9 +370,8 @@ internal class SceneGestureHandler(

            if (startFromIdlePosition) {
                // If there is a target scene, we start the overscroll animation.
                val result =
                    findUserActionResultStrict(velocity)
                        ?: run {
                val result = swipes!!.findUserActionResultStrict(velocity)
                if (result == null) {
                    // We will not animate
                    layoutState.finishTransition(swipeTransition, idleScene = fromScene.key)
                    return
@@ -447,8 +381,7 @@ internal class SceneGestureHandler(
                    SwipeTransition(
                            fromScene = fromScene,
                            result = result,
                            upOrLeftResult = upOrLeftResult,
                            downOrRightResult = downOrRightResult,
                            swipes = swipes!!,
                            layoutImpl = layoutImpl,
                            orientation = orientation
                        )
@@ -506,11 +439,12 @@ internal class SceneGestureHandler(
private fun SwipeTransition(
    fromScene: Scene,
    result: UserActionResult,
    upOrLeftResult: UserActionResult?,
    downOrRightResult: UserActionResult?,
    swipes: Swipes,
    layoutImpl: SceneTransitionLayoutImpl,
    orientation: Orientation,
): SwipeTransition {
    val upOrLeftResult = swipes.upOrLeftResult
    val downOrRightResult = swipes.downOrRightResult
    val userActionDistance = result.distance ?: DefaultSwipeDistance
    val absoluteDistance =
        with(userActionDistance) {
@@ -643,7 +577,75 @@ private class Swipes(
    val downOrRight: Swipe?,
    val upOrLeftNoSource: Swipe?,
    val downOrRightNoSource: Swipe?,
)
) {
    /** The [UserActionResult] associated to up and down swipes. */
    var upOrLeftResult: UserActionResult? = null
    var downOrRightResult: UserActionResult? = null

    fun computeSwipesResults(fromScene: Scene): Pair<UserActionResult?, UserActionResult?> {
        val userActions = fromScene.userActions
        fun result(swipe: Swipe?): UserActionResult? {
            return userActions[swipe ?: return null]
        }

        val upOrLeftResult = result(upOrLeft) ?: result(upOrLeftNoSource)
        val downOrRightResult = result(downOrRight) ?: result(downOrRightNoSource)
        return upOrLeftResult to downOrRightResult
    }

    fun updateSwipesResults(fromScene: Scene) {
        val (upOrLeftResult, downOrRightResult) = computeSwipesResults(fromScene)

        this.upOrLeftResult = upOrLeftResult
        this.downOrRightResult = downOrRightResult
    }

    /**
     * Returns the [UserActionResult] from [fromScene] in the direction of [directionOffset].
     *
     * @param fromScene the scene from which we look for the target
     * @param directionOffset signed float that indicates the direction. Positive is down or right
     *   negative is up or left.
     * @param updateSwipesResults whether the target scenes should be updated to the current values
     *   held in the Scenes map. Usually we don't want to update them while doing a drag, because
     *   this could change the target scene (jump cutting) to a different scene, when some system
     *   state changed the targets the background. However, an update is needed any time we
     *   calculate the targets for a new fromScene.
     * @return null when there are no targets in either direction. If one direction is null and you
     *   drag into the null direction this function will return the opposite direction, assuming
     *   that the users intention is to start the drag into the other direction eventually. If
     *   [directionOffset] is 0f and both direction are available, it will default to
     *   [upOrLeftResult].
     */
    fun findUserActionResult(
        fromScene: Scene,
        directionOffset: Float,
        updateSwipesResults: Boolean,
    ): UserActionResult? {
        if (updateSwipesResults) {
            updateSwipesResults(fromScene)
        }

        return when {
            upOrLeftResult == null && downOrRightResult == null -> null
            (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null ->
                upOrLeftResult
            else -> downOrRightResult
        }
    }

    /**
     * A strict version of [findUserActionResult] that will return null when there is no Scene in
     * [directionOffset] direction
     */
    fun findUserActionResultStrict(directionOffset: Float): UserActionResult? {
        return when {
            directionOffset > 0f -> upOrLeftResult
            directionOffset < 0f -> downOrRightResult
            else -> null
        }
    }
}

private class SceneDraggableHandler(
    private val gestureHandler: SceneGestureHandler,