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

Commit 43de60f2 authored by omarmt's avatar omarmt
Browse files

Moved NestedScrollConnection into SwipeToScene

This change allows us to consume all nested scroll gestures of the
scrollable elements in the scene without having to specify an additional
 modifier above the scrollable components.
The SceneTransitionLayout now has a default nested scroll behavior that
can be changed via the previous nestedScrollToScene() modifier.

Test: atest NestedScrollToSceneTest
Test: atest ElementTest (nestedScrollToScene is not mandatory anymore)
Test: Manually tested on Flexiglass
Bug: 336710600
Flag: com.android.systemui.scene_container
Change-Id: Icade65f33b84a9784aef599a62d0485dc65088db
parent b29945a6
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -911,9 +911,9 @@ private class Swipes(
internal class NestedScrollHandlerImpl(
    private val layoutImpl: SceneTransitionLayoutImpl,
    private val orientation: Orientation,
    private val topOrLeftBehavior: NestedScrollBehavior,
    private val bottomOrRightBehavior: NestedScrollBehavior,
    private val isExternalOverscrollGesture: () -> Boolean,
    internal var topOrLeftBehavior: NestedScrollBehavior,
    internal var bottomOrRightBehavior: NestedScrollBehavior,
    internal var isExternalOverscrollGesture: () -> Boolean,
    private val pointersInfoOwner: PointersInfoOwner,
) {
    private val layoutState = layoutImpl.state
+1 −6
Original line number Diff line number Diff line
@@ -41,7 +41,6 @@ import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.ObserverModifierNode
import androidx.compose.ui.node.PointerInputModifierNode
import androidx.compose.ui.node.TraversableNode
import androidx.compose.ui.node.currentValueOf
import androidx.compose.ui.node.findNearestAncestor
import androidx.compose.ui.node.observeReads
@@ -139,16 +138,12 @@ internal class MultiPointerDraggableNode(
    DelegatingNode(),
    PointerInputModifierNode,
    CompositionLocalConsumerModifierNode,
    TraversableNode,
    PointersInfoOwner,
    ObserverModifierNode {
    private val pointerInputHandler: suspend PointerInputScope.() -> Unit = { pointerInput() }
    private val delegate = delegate(SuspendingPointerInputModifierNode(pointerInputHandler))
    private val velocityTracker = VelocityTracker()
    private var previousEnabled: Boolean = false

    override val traverseKey: Any = TRAVERSE_KEY

    var enabled: () -> Boolean = enabled
        set(value) {
            // Reset the pointer input whenever enabled changed.
@@ -208,7 +203,7 @@ internal class MultiPointerDraggableNode(
    private var startedPosition: Offset? = null
    private var pointersDown: Int = 0

    override fun pointersInfo(): PointersInfo {
    internal fun pointersInfo(): PointersInfo {
        return PointersInfo(
            startedPosition = startedPosition,
            // Note: We could have 0 pointers during fling or for other reasons.
+8 −72
Original line number Diff line number Diff line
@@ -22,11 +22,9 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScrollModifierNode
import androidx.compose.ui.node.DelegatableNode
import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.InspectorInfo
import com.android.compose.nestedscroll.PriorityNestedScrollConnection

/**
 * Defines the behavior of the [SceneTransitionLayout] when a scrollable component is scrolled.
@@ -70,7 +68,11 @@ enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) {
     * In addition, during scene transitions, scroll events are consumed by the
     * [SceneTransitionLayout] instead of the scrollable component.
     */
    EdgeAlways(canStartOnPostFling = true),
    EdgeAlways(canStartOnPostFling = true);

    companion object {
        val Default = EdgeNoPreview
    }
}

internal fun Modifier.nestedScrollToScene(
@@ -131,7 +133,6 @@ private class NestedScrollToSceneNode(
    private var bottomOrRightBehavior: NestedScrollBehavior,
    private var isExternalOverscrollGesture: () -> Boolean,
) : DelegatingNode() {
    lateinit var pointersInfoOwner: PointersInfoOwner
    private var scrollBehaviorOwner: ScrollBehaviorOwner? = null

    private fun requireScrollBehaviorOwner(): ScrollBehaviorOwner {
@@ -143,7 +144,7 @@ private class NestedScrollToSceneNode(
        return behaviorOwner
    }

    val updateScrollBehaviorsConnection =
    private val updateScrollBehaviorsConnection =
        object : NestedScrollConnection {
            /**
             * When using [NestedScrollConnection.onPostScroll], we can specify the desired behavior
@@ -174,37 +175,11 @@ private class NestedScrollToSceneNode(
            }
        }

    private var priorityNestedScrollConnection: PriorityNestedScrollConnection =
        scenePriorityNestedScrollConnection(
            layoutImpl = layoutImpl,
            orientation = orientation,
            topOrLeftBehavior = topOrLeftBehavior,
            bottomOrRightBehavior = bottomOrRightBehavior,
            isExternalOverscrollGesture = isExternalOverscrollGesture,
            pointersInfoOwner = { pointersInfoOwner.pointersInfo() }
        )

    private var nestedScrollNode: DelegatableNode =
        nestedScrollModifierNode(
            connection = priorityNestedScrollConnection,
            dispatcher = null,
        )

    private var updateScrollBehaviorsNestedScrollNode: DelegatableNode =
        nestedScrollModifierNode(
            connection = updateScrollBehaviorsConnection,
            dispatcher = null,
        )

    override fun onAttach() {
        pointersInfoOwner = requireAncestorPointersInfoOwner()
        delegate(nestedScrollNode)
        delegate(updateScrollBehaviorsNestedScrollNode)
    init {
        delegate(nestedScrollModifierNode(updateScrollBehaviorsConnection, dispatcher = null))
    }

    override fun onDetach() {
        // Make sure we reset the scroll connection when this modifier is removed from composition
        priorityNestedScrollConnection.reset()
        scrollBehaviorOwner = null
    }

@@ -220,44 +195,5 @@ private class NestedScrollToSceneNode(
        this.topOrLeftBehavior = topOrLeftBehavior
        this.bottomOrRightBehavior = bottomOrRightBehavior
        this.isExternalOverscrollGesture = isExternalOverscrollGesture

        // Clean up the old nested scroll connection
        priorityNestedScrollConnection.reset()
        undelegate(nestedScrollNode)

        // Create a new nested scroll connection
        priorityNestedScrollConnection =
            scenePriorityNestedScrollConnection(
                layoutImpl = layoutImpl,
                orientation = orientation,
                topOrLeftBehavior = topOrLeftBehavior,
                bottomOrRightBehavior = bottomOrRightBehavior,
                isExternalOverscrollGesture = isExternalOverscrollGesture,
                pointersInfoOwner = pointersInfoOwner,
            )
        nestedScrollNode =
            nestedScrollModifierNode(
                connection = priorityNestedScrollConnection,
                dispatcher = null,
            )
        delegate(nestedScrollNode)
    }
}

private fun scenePriorityNestedScrollConnection(
    layoutImpl: SceneTransitionLayoutImpl,
    orientation: Orientation,
    topOrLeftBehavior: NestedScrollBehavior,
    bottomOrRightBehavior: NestedScrollBehavior,
    isExternalOverscrollGesture: () -> Boolean,
    pointersInfoOwner: PointersInfoOwner,
) =
    NestedScrollHandlerImpl(
            layoutImpl = layoutImpl,
            orientation = orientation,
            topOrLeftBehavior = topOrLeftBehavior,
            bottomOrRightBehavior = bottomOrRightBehavior,
            isExternalOverscrollGesture = isExternalOverscrollGesture,
            pointersInfoOwner = pointersInfoOwner,
        )
        .connection
+4 −4
Original line number Diff line number Diff line
@@ -204,8 +204,8 @@ interface BaseSceneScope : ElementStateScope {
     * @param rightBehavior when we should perform the overscroll animation at the right.
     */
    fun Modifier.horizontalNestedScrollToScene(
        leftBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
        rightBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
        leftBehavior: NestedScrollBehavior = NestedScrollBehavior.Default,
        rightBehavior: NestedScrollBehavior = NestedScrollBehavior.Default,
        isExternalOverscrollGesture: () -> Boolean = { false },
    ): Modifier

@@ -217,8 +217,8 @@ interface BaseSceneScope : ElementStateScope {
     * @param bottomBehavior when we should perform the overscroll animation at the bottom.
     */
    fun Modifier.verticalNestedScrollToScene(
        topBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
        bottomBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
        topBehavior: NestedScrollBehavior = NestedScrollBehavior.Default,
        bottomBehavior: NestedScrollBehavior = NestedScrollBehavior.Default,
        isExternalOverscrollGesture: () -> Boolean = { false },
    ): Modifier

+26 −5
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.nestedScrollModifierNode
import androidx.compose.ui.input.pointer.PointerEvent
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.node.DelegatableNode
@@ -81,8 +82,24 @@ private class SwipeToSceneNode(
            }
        }

    override fun onAttach() {
        delegate(ScrollBehaviorOwnerNode(draggableHandler.nestedScrollKey))
    private val nestedScrollHandlerImpl =
        NestedScrollHandlerImpl(
            layoutImpl = draggableHandler.layoutImpl,
            orientation = draggableHandler.orientation,
            topOrLeftBehavior = NestedScrollBehavior.Default,
            bottomOrRightBehavior = NestedScrollBehavior.Default,
            isExternalOverscrollGesture = { false },
            pointersInfoOwner = { multiPointerDraggableNode.pointersInfo() },
        )

    init {
        delegate(nestedScrollModifierNode(nestedScrollHandlerImpl.connection, dispatcher = null))
        delegate(ScrollBehaviorOwnerNode(draggableHandler.nestedScrollKey, nestedScrollHandlerImpl))
    }

    override fun onDetach() {
        // Make sure we reset the scroll connection when this modifier is removed from composition
        nestedScrollHandlerImpl.connection.reset()
    }

    override fun onPointerEvent(
@@ -149,13 +166,17 @@ internal fun interface ScrollBehaviorOwner {
 *
 * TODO(b/353234530) move this logic into [SwipeToSceneNode]
 */
private class ScrollBehaviorOwnerNode(override val traverseKey: Any) :
    Modifier.Node(), TraversableNode, ScrollBehaviorOwner {
private class ScrollBehaviorOwnerNode(
    override val traverseKey: Any,
    val nestedScrollHandlerImpl: NestedScrollHandlerImpl
) : Modifier.Node(), TraversableNode, ScrollBehaviorOwner {
    override fun updateScrollBehaviors(
        topOrLeftBehavior: NestedScrollBehavior,
        bottomOrRightBehavior: NestedScrollBehavior,
        isExternalOverscrollGesture: () -> Boolean
    ) {
        // This method will be used to update the desired behavior in a following CL.
        nestedScrollHandlerImpl.topOrLeftBehavior = topOrLeftBehavior
        nestedScrollHandlerImpl.bottomOrRightBehavior = bottomOrRightBehavior
        nestedScrollHandlerImpl.isExternalOverscrollGesture = isExternalOverscrollGesture
    }
}
Loading