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

Commit b21a315e authored by omarmt's avatar omarmt
Browse files

Revert "Expose startedPosition and pointersDown to NestedScrollHandler"

This reverts commit 9a42e828.

Reason for revert: Bug in STL transition

Test: no test
Bug: 330200163
Flag: com.android.systemui.scene_container
Change-Id: I4319ad2138d7e4e958733e6610eb1ac26f701a06
parent f6ec228d
Loading
Loading
Loading
Loading
+24 −28
Original line number Diff line number Diff line
@@ -913,7 +913,6 @@ internal class NestedScrollHandlerImpl(
    private val topOrLeftBehavior: NestedScrollBehavior,
    private val bottomOrRightBehavior: NestedScrollBehavior,
    private val isExternalOverscrollGesture: () -> Boolean,
    private val pointersInfo: () -> PointersInfo,
) {
    private val layoutState = layoutImpl.state
    private val draggableHandler = layoutImpl.draggableHandler(orientation)
@@ -925,13 +924,6 @@ internal class NestedScrollHandlerImpl(
        // moving on to the next scene.
        var canChangeScene = false

        fun hasNextScene(amount: Float): Boolean {
            val transitionState = layoutState.transitionState
            val scene = transitionState.currentScene
            val fromScene = layoutImpl.scene(scene)
            val nextScene =
                when {
                    amount < 0f -> {
        val actionUpOrLeft =
            Swipe(
                direction =
@@ -939,11 +931,9 @@ internal class NestedScrollHandlerImpl(
                        Orientation.Horizontal -> SwipeDirection.Left
                        Orientation.Vertical -> SwipeDirection.Up
                    },
                                pointerCount = pointersInfo().pointersDown,
                pointerCount = 1,
            )
                        fromScene.userActions[actionUpOrLeft]
                    }
                    amount > 0f -> {

        val actionDownOrRight =
            Swipe(
                direction =
@@ -951,10 +941,17 @@ internal class NestedScrollHandlerImpl(
                        Orientation.Horizontal -> SwipeDirection.Right
                        Orientation.Vertical -> SwipeDirection.Down
                    },
                                pointerCount = pointersInfo().pointersDown,
                pointerCount = 1,
            )
                        fromScene.userActions[actionDownOrRight]
                    }

        fun hasNextScene(amount: Float): Boolean {
            val transitionState = layoutState.transitionState
            val scene = transitionState.currentScene
            val fromScene = layoutImpl.scene(scene)
            val nextScene =
                when {
                    amount < 0f -> fromScene.userActions[actionUpOrLeft]
                    amount > 0f -> fromScene.userActions[actionDownOrRight]
                    else -> null
                }
            if (nextScene != null) return true
@@ -1052,11 +1049,10 @@ internal class NestedScrollHandlerImpl(
            canContinueScroll = { true },
            canScrollOnFling = false,
            onStart = { offsetAvailable ->
                val pointers = pointersInfo()
                dragController =
                    draggableHandler.onDragStarted(
                        pointersDown = pointers.pointersDown,
                        startedPosition = pointers.startedPosition,
                        pointersDown = 1,
                        startedPosition = null,
                        overSlop = if (isIntercepting) 0f else offsetAvailable,
                    )
            },
+11 −53
Original line number Diff line number Diff line
@@ -18,21 +18,12 @@ package com.android.compose.animation.scene

import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.nestedScrollModifierNode
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.PointerInputScope
import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
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 androidx.compose.ui.util.fastMap
import androidx.compose.ui.util.fastReduce
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.isActive

/**
 * Defines the behavior of the [SceneTransitionLayout] when a scrollable component is scrolled.
@@ -130,11 +121,6 @@ private data class NestedScrollToSceneElement(
    }
}

internal data class PointersInfo(
    val pointersDown: Int,
    val startedPosition: Offset,
)

private class NestedScrollToSceneNode(
    layoutImpl: SceneTransitionLayoutImpl,
    orientation: Orientation,
@@ -149,48 +135,22 @@ private class NestedScrollToSceneNode(
            topOrLeftBehavior = topOrLeftBehavior,
            bottomOrRightBehavior = bottomOrRightBehavior,
            isExternalOverscrollGesture = isExternalOverscrollGesture,
            pointersInfo = pointerInfo()
        )

    private var lastPointers: List<PointerInputChange>? = null

    private fun pointerInfo(): () -> PointersInfo = {
        val pointers =
            requireNotNull(lastPointers) { "NestedScroll API was called before PointerInput API" }
        PointersInfo(
            pointersDown = pointers.size,
            startedPosition = pointers.fastMap { it.position }.fastReduce { a, b -> (a + b) / 2f },
    private var nestedScrollNode: DelegatableNode =
        nestedScrollModifierNode(
            connection = priorityNestedScrollConnection,
            dispatcher = null,
        )
    }

    private val pointerInputHandler: suspend PointerInputScope.() -> Unit = {
        coroutineScope {
            awaitPointerEventScope {
                // Await this scope to guarantee that the PointerInput API receives touch events
                // before the NestedScroll API.
    override fun onAttach() {
        delegate(nestedScrollNode)

                try {
                    while (isActive) {
                        // During the initial phase, we receive the event after our ancestors.
                        lastPointers = awaitPointerEvent(PointerEventPass.Initial).changes
    }
                } finally {
                    // Clean up the nested scroll connection

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

    private val pointerInputNode = delegate(SuspendingPointerInputModifierNode(pointerInputHandler))

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

    fun update(
        layoutImpl: SceneTransitionLayoutImpl,
@@ -201,7 +161,7 @@ private class NestedScrollToSceneNode(
    ) {
        // Clean up the old nested scroll connection
        priorityNestedScrollConnection.reset()
        pointerInputNode.resetPointerInputHandler()
        undelegate(nestedScrollNode)

        // Create a new nested scroll connection
        priorityNestedScrollConnection =
@@ -211,13 +171,13 @@ private class NestedScrollToSceneNode(
                topOrLeftBehavior = topOrLeftBehavior,
                bottomOrRightBehavior = bottomOrRightBehavior,
                isExternalOverscrollGesture = isExternalOverscrollGesture,
                pointersInfo = pointerInfo(),
            )
        nestedScrollNode =
            nestedScrollModifierNode(
                connection = priorityNestedScrollConnection,
                dispatcher = null,
            )
        delegate(nestedScrollNode)
    }
}

@@ -227,7 +187,6 @@ private fun scenePriorityNestedScrollConnection(
    topOrLeftBehavior: NestedScrollBehavior,
    bottomOrRightBehavior: NestedScrollBehavior,
    isExternalOverscrollGesture: () -> Boolean,
    pointersInfo: () -> PointersInfo,
) =
    NestedScrollHandlerImpl(
            layoutImpl = layoutImpl,
@@ -235,6 +194,5 @@ private fun scenePriorityNestedScrollConnection(
            topOrLeftBehavior = topOrLeftBehavior,
            bottomOrRightBehavior = bottomOrRightBehavior,
            isExternalOverscrollGesture = isExternalOverscrollGesture,
            pointersInfo = pointersInfo,
        )
        .connection
+1 −2
Original line number Diff line number Diff line
@@ -113,8 +113,7 @@ class DraggableHandlerTest {
                    orientation = draggableHandler.orientation,
                    topOrLeftBehavior = nestedScrollBehavior,
                    bottomOrRightBehavior = nestedScrollBehavior,
                    isExternalOverscrollGesture = { isExternalOverscrollGesture },
                    pointersInfo = { PointersInfo(pointersDown = 1, startedPosition = Offset.Zero) }
                    isExternalOverscrollGesture = { isExternalOverscrollGesture }
                )
                .connection

+0 −74
Original line number Diff line number Diff line
@@ -838,80 +838,6 @@ class ElementTest {
        fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 1.5f)
    }

    @Test
    fun elementTransitionDuringNestedScrollWith2Pointers() {
        // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
        // detected as a drag event.
        var touchSlop = 0f
        val translateY = 10.dp
        val layoutWidth = 200.dp
        val layoutHeight = 400.dp

        val state =
            rule.runOnUiThread {
                MutableSceneTransitionLayoutState(
                    initialScene = SceneA,
                    transitions =
                        transitions {
                            from(SceneA, to = SceneB) {
                                translate(TestElements.Foo, y = translateY)
                            }
                        },
                )
                    as MutableSceneTransitionLayoutStateImpl
            }

        rule.setContent {
            touchSlop = LocalViewConfiguration.current.touchSlop
            SceneTransitionLayout(
                state = state,
                modifier = Modifier.size(layoutWidth, layoutHeight)
            ) {
                scene(
                    SceneA,
                    userActions = mapOf(Swipe(SwipeDirection.Down, pointerCount = 2) to SceneB)
                ) {
                    Box(
                        Modifier
                            // Unconsumed scroll gesture will be intercepted by STL
                            .verticalNestedScrollToScene()
                            // A scrollable that does not consume the scroll gesture
                            .scrollable(
                                rememberScrollableState(consumeScrollDelta = { 0f }),
                                Orientation.Vertical
                            )
                            .fillMaxSize()
                    ) {
                        Spacer(Modifier.element(TestElements.Foo).fillMaxSize())
                    }
                }
                scene(SceneB) { Spacer(Modifier.fillMaxSize()) }
            }
        }

        assertThat(state.transitionState).isIdle()
        val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
        fooElement.assertTopPositionInRootIsEqualTo(0.dp)

        // Swipe down with 2 pointers by half of verticalSwipeDistance.
        rule.onRoot().performTouchInput {
            val middleTop = Offset((layoutWidth / 2).toPx(), 0f)
            repeat(2) { i -> down(pointerId = i, middleTop) }
            repeat(2) { i ->
                // Scroll 50%
                moveBy(
                    pointerId = i,
                    delta = Offset(0f, touchSlop + layoutHeight.toPx() * 0.5f),
                    delayMillis = 1_000,
                )
            }
        }

        val transition = assertThat(state.transitionState).isTransition()
        assertThat(transition).hasProgress(0.5f)
        fooElement.assertTopPositionInRootIsEqualTo(translateY * 0.5f)
    }

    @Test
    fun elementTransitionWithDistanceDuringOverscroll() {
        val layoutWidth = 200.dp