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

Commit 9835d090 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Revert^2 "Add support for swipes started from an edge"

02590bd9

Change-Id: I10dabf4ce995943b5e3fe0ec912e415b0274f5be
parent d5bdc87d
Loading
Loading
Loading
Loading
+75 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.compose.animation.scene

import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp

interface EdgeDetector {
    /**
     * Return the [Edge] associated to [position] inside a layout of size [layoutSize], given
     * [density] and [orientation].
     */
    fun edge(
        layoutSize: IntSize,
        position: IntOffset,
        density: Density,
        orientation: Orientation,
    ): Edge?
}

val DefaultEdgeDetector = FixedSizeEdgeDetector(40.dp)

/** An [EdgeDetector] that detects edges assuming a fixed edge size of [size]. */
class FixedSizeEdgeDetector(val size: Dp) : EdgeDetector {
    override fun edge(
        layoutSize: IntSize,
        position: IntOffset,
        density: Density,
        orientation: Orientation,
    ): Edge? {
        val axisSize: Int
        val axisPosition: Int
        val topOrLeft: Edge
        val bottomOrRight: Edge
        when (orientation) {
            Orientation.Horizontal -> {
                axisSize = layoutSize.width
                axisPosition = position.x
                topOrLeft = Edge.Left
                bottomOrRight = Edge.Right
            }
            Orientation.Vertical -> {
                axisSize = layoutSize.height
                axisPosition = position.y
                topOrLeft = Edge.Top
                bottomOrRight = Edge.Bottom
            }
        }

        val sizePx = with(density) { size.toPx() }
        return when {
            axisPosition <= sizePx -> topOrLeft
            axisPosition >= axisSize - sizePx -> bottomOrRight
            else -> null
        }
    }
}
+5 −1
Original line number Original line Diff line number Diff line
@@ -38,6 +38,7 @@ import androidx.compose.ui.platform.LocalDensity
 *   instance by triggering back navigation or by swiping to a new scene.
 *   instance by triggering back navigation or by swiping to a new scene.
 * @param transitions the definition of the transitions used to animate a change of scene.
 * @param transitions the definition of the transitions used to animate a change of scene.
 * @param state the observable state of this layout.
 * @param state the observable state of this layout.
 * @param edgeDetector the edge detector used to detect which edge a swipe is started from, if any.
 * @param scenes the configuration of the different scenes of this layout.
 * @param scenes the configuration of the different scenes of this layout.
 */
 */
@Composable
@Composable
@@ -47,6 +48,7 @@ fun SceneTransitionLayout(
    transitions: SceneTransitions,
    transitions: SceneTransitions,
    modifier: Modifier = Modifier,
    modifier: Modifier = Modifier,
    state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) },
    state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) },
    edgeDetector: EdgeDetector = DefaultEdgeDetector,
    scenes: SceneTransitionLayoutScope.() -> Unit,
    scenes: SceneTransitionLayoutScope.() -> Unit,
) {
) {
    val density = LocalDensity.current
    val density = LocalDensity.current
@@ -57,15 +59,17 @@ fun SceneTransitionLayout(
            transitions,
            transitions,
            state,
            state,
            density,
            density,
            edgeDetector,
        )
        )
    }
    }


    layoutImpl.onChangeScene = onChangeScene
    layoutImpl.onChangeScene = onChangeScene
    layoutImpl.transitions = transitions
    layoutImpl.transitions = transitions
    layoutImpl.density = density
    layoutImpl.density = density
    layoutImpl.edgeDetector = edgeDetector

    layoutImpl.setScenes(scenes)
    layoutImpl.setScenes(scenes)
    layoutImpl.setCurrentScene(currentScene)
    layoutImpl.setCurrentScene(currentScene)

    layoutImpl.Content(modifier)
    layoutImpl.Content(modifier)
}
}


+2 −0
Original line number Original line Diff line number Diff line
@@ -47,6 +47,7 @@ class SceneTransitionLayoutImpl(
    transitions: SceneTransitions,
    transitions: SceneTransitions,
    internal val state: SceneTransitionLayoutState,
    internal val state: SceneTransitionLayoutState,
    density: Density,
    density: Density,
    edgeDetector: EdgeDetector,
) {
) {
    internal val scenes = SnapshotStateMap<SceneKey, Scene>()
    internal val scenes = SnapshotStateMap<SceneKey, Scene>()
    internal val elements = SnapshotStateMap<ElementKey, Element>()
    internal val elements = SnapshotStateMap<ElementKey, Element>()
@@ -57,6 +58,7 @@ class SceneTransitionLayoutImpl(
    internal var onChangeScene by mutableStateOf(onChangeScene)
    internal var onChangeScene by mutableStateOf(onChangeScene)
    internal var transitions by mutableStateOf(transitions)
    internal var transitions by mutableStateOf(transitions)
    internal var density: Density by mutableStateOf(density)
    internal var density: Density by mutableStateOf(density)
    internal var edgeDetector by mutableStateOf(edgeDetector)


    /**
    /**
     * The size of this layout. Note that this could be [IntSize.Zero] if this layour does not have
     * The size of this layout. Note that this could be [IntSize.Zero] if this layour does not have
+42 −11
Original line number Original line Diff line number Diff line
@@ -35,6 +35,7 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import kotlin.math.absoluteValue
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineScope
@@ -157,7 +158,7 @@ class SceneGestureHandler(


    internal var gestureWithPriority: Any? = null
    internal var gestureWithPriority: Any? = null


    internal fun onDragStarted(pointersDown: Int) {
    internal fun onDragStarted(pointersDown: Int, startedPosition: Offset?) {
        if (isDrivingTransition) {
        if (isDrivingTransition) {
            // This [transition] was already driving the animation: simply take over it.
            // This [transition] was already driving the animation: simply take over it.
            // Stop animating and start from where the current offset.
            // Stop animating and start from where the current offset.
@@ -197,6 +198,16 @@ class SceneGestureHandler(
                Orientation.Vertical -> layoutImpl.size.height
                Orientation.Vertical -> layoutImpl.size.height
            }.toFloat()
            }.toFloat()


        val fromEdge =
            startedPosition?.let { position ->
                layoutImpl.edgeDetector.edge(
                    layoutImpl.size,
                    position.round(),
                    layoutImpl.density,
                    orientation,
                )
            }

        swipeTransition.actionUpOrLeft =
        swipeTransition.actionUpOrLeft =
            Swipe(
            Swipe(
                direction =
                direction =
@@ -205,6 +216,7 @@ class SceneGestureHandler(
                        Orientation.Vertical -> SwipeDirection.Up
                        Orientation.Vertical -> SwipeDirection.Up
                    },
                    },
                pointerCount = pointersDown,
                pointerCount = pointersDown,
                fromEdge = fromEdge,
            )
            )


        swipeTransition.actionDownOrRight =
        swipeTransition.actionDownOrRight =
@@ -215,8 +227,19 @@ class SceneGestureHandler(
                        Orientation.Vertical -> SwipeDirection.Down
                        Orientation.Vertical -> SwipeDirection.Down
                    },
                    },
                pointerCount = pointersDown,
                pointerCount = pointersDown,
                fromEdge = fromEdge,
            )
            )


        if (fromEdge == null) {
            swipeTransition.actionUpOrLeftNoEdge = null
            swipeTransition.actionDownOrRightNoEdge = null
        } else {
            swipeTransition.actionUpOrLeftNoEdge =
                (swipeTransition.actionUpOrLeft as Swipe).copy(fromEdge = null)
            swipeTransition.actionDownOrRightNoEdge =
                (swipeTransition.actionDownOrRight as Swipe).copy(fromEdge = null)
        }

        if (swipeTransition.absoluteDistance > 0f) {
        if (swipeTransition.absoluteDistance > 0f) {
            transitionState = swipeTransition
            transitionState = swipeTransition
        }
        }
@@ -264,15 +287,11 @@ class SceneGestureHandler(
        // to the next screen or go back to the previous one.
        // to the next screen or go back to the previous one.
        val offset = swipeTransition.dragOffset
        val offset = swipeTransition.dragOffset
        val absoluteDistance = swipeTransition.absoluteDistance
        val absoluteDistance = swipeTransition.absoluteDistance
        if (
        if (offset <= -absoluteDistance && swipeTransition.upOrLeft(fromScene) == toScene.key) {
            offset <= -absoluteDistance &&
                fromScene.userActions[swipeTransition.actionUpOrLeft] == toScene.key
        ) {
            swipeTransition.dragOffset += absoluteDistance
            swipeTransition.dragOffset += absoluteDistance
            swipeTransition._fromScene = toScene
            swipeTransition._fromScene = toScene
        } else if (
        } else if (
            offset >= absoluteDistance &&
            offset >= absoluteDistance && swipeTransition.downOrRight(fromScene) == toScene.key
                fromScene.userActions[swipeTransition.actionDownOrRight] == toScene.key
        ) {
        ) {
            swipeTransition.dragOffset -= absoluteDistance
            swipeTransition.dragOffset -= absoluteDistance
            swipeTransition._fromScene = toScene
            swipeTransition._fromScene = toScene
@@ -294,8 +313,8 @@ class SceneGestureHandler(
                Orientation.Vertical -> layoutImpl.size.height
                Orientation.Vertical -> layoutImpl.size.height
            }.toFloat()
            }.toFloat()


        val upOrLeft = userActions[swipeTransition.actionUpOrLeft]
        val upOrLeft = swipeTransition.upOrLeft(this)
        val downOrRight = userActions[swipeTransition.actionDownOrRight]
        val downOrRight = swipeTransition.downOrRight(this)


        // Compute the target scene depending on the current offset.
        // Compute the target scene depending on the current offset.
        return when {
        return when {
@@ -542,6 +561,18 @@ class SceneGestureHandler(
        /** The [UserAction]s associated to this swipe. */
        /** The [UserAction]s associated to this swipe. */
        var actionUpOrLeft: UserAction = Back
        var actionUpOrLeft: UserAction = Back
        var actionDownOrRight: UserAction = Back
        var actionDownOrRight: UserAction = Back
        var actionUpOrLeftNoEdge: UserAction? = null
        var actionDownOrRightNoEdge: UserAction? = null

        fun upOrLeft(scene: Scene): SceneKey? {
            return scene.userActions[actionUpOrLeft]
                ?: actionUpOrLeftNoEdge?.let { scene.userActions[it] }
        }

        fun downOrRight(scene: Scene): SceneKey? {
            return scene.userActions[actionDownOrRight]
                ?: actionDownOrRightNoEdge?.let { scene.userActions[it] }
        }
    }
    }


    companion object {
    companion object {
@@ -554,7 +585,7 @@ private class SceneDraggableHandler(
) : DraggableHandler {
) : DraggableHandler {
    override fun onDragStarted(startedPosition: Offset, pointersDown: Int) {
    override fun onDragStarted(startedPosition: Offset, pointersDown: Int) {
        gestureHandler.gestureWithPriority = this
        gestureHandler.gestureWithPriority = this
        gestureHandler.onDragStarted(pointersDown)
        gestureHandler.onDragStarted(pointersDown, startedPosition)
    }
    }


    override fun onDelta(pixels: Float) {
    override fun onDelta(pixels: Float) {
@@ -671,7 +702,7 @@ class SceneNestedScrollHandler(
            onStart = {
            onStart = {
                gestureHandler.gestureWithPriority = this
                gestureHandler.gestureWithPriority = this
                priorityScene = nextScene
                priorityScene = nextScene
                gestureHandler.onDragStarted(pointersDown = 1)
                gestureHandler.onDragStarted(pointersDown = 1, startedPosition = null)
            },
            },
            onScroll = { offsetAvailable ->
            onScroll = { offsetAvailable ->
                if (gestureHandler.gestureWithPriority != this) {
                if (gestureHandler.gestureWithPriority != this) {
+70 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.compose.animation.scene

import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class FixedSizeEdgeDetectorTest {
    private val detector = FixedSizeEdgeDetector(30.dp)
    private val layoutSize = IntSize(100, 100)
    private val density = Density(1f)

    @Test
    fun horizontalEdges() {
        fun horizontalEdge(position: Int): Edge? =
            detector.edge(
                layoutSize,
                position = IntOffset(position, 0),
                density,
                Orientation.Horizontal,
            )

        assertThat(horizontalEdge(0)).isEqualTo(Edge.Left)
        assertThat(horizontalEdge(30)).isEqualTo(Edge.Left)
        assertThat(horizontalEdge(31)).isEqualTo(null)
        assertThat(horizontalEdge(69)).isEqualTo(null)
        assertThat(horizontalEdge(70)).isEqualTo(Edge.Right)
        assertThat(horizontalEdge(100)).isEqualTo(Edge.Right)
    }

    @Test
    fun verticalEdges() {
        fun verticalEdge(position: Int): Edge? =
            detector.edge(
                layoutSize,
                position = IntOffset(0, position),
                density,
                Orientation.Vertical,
            )

        assertThat(verticalEdge(0)).isEqualTo(Edge.Top)
        assertThat(verticalEdge(30)).isEqualTo(Edge.Top)
        assertThat(verticalEdge(31)).isEqualTo(null)
        assertThat(verticalEdge(69)).isEqualTo(null)
        assertThat(verticalEdge(70)).isEqualTo(Edge.Bottom)
        assertThat(verticalEdge(100)).isEqualTo(Edge.Bottom)
    }
}
Loading