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

Commit a473c365 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes Ie3cd89b7,I9af3f5eb into main

* changes:
  Handle cancellation in NestedDraggableNode.trackDownPosition()
  Add pointerType to NestedDraggable.onDragStarted()
parents af5b6ce5 2b08b992
Loading
Loading
Loading
Loading
+34 −20
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.PointerId
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.PointerInputScope
import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed
import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
@@ -52,7 +53,6 @@ import androidx.compose.ui.node.currentValueOf
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.util.fastSumBy
import com.android.compose.modifiers.thenIf
import kotlin.math.sign
import kotlinx.coroutines.CompletableDeferred
@@ -81,7 +81,13 @@ interface NestedDraggable {
     * in the direction given by [sign], with the given number of [pointersDown] when the touch slop
     * was detected.
     */
    fun onDragStarted(position: Offset, sign: Float, pointersDown: Int): Controller
    fun onDragStarted(
        position: Offset,
        sign: Float,
        pointersDown: Int,
        // TODO(b/382665591): Make this non-nullable.
        pointerType: PointerType?,
    ): Controller

    /**
     * Whether this draggable should consume any scroll amount with the given [sign] coming from a
@@ -184,8 +190,8 @@ private class NestedDraggableNode(
     */
    private var lastFirstDown: Offset? = null

    /** The number of pointers down. */
    private var pointersDownCount = 0
    /** The pointers currently down, in order of which they were done and mapping to their type. */
    private val pointersDown = linkedMapOf<PointerId, PointerType>()

    init {
        delegate(nestedScrollModifierNode(this, nestedScrollDispatcher))
@@ -256,7 +262,9 @@ private class NestedDraggableNode(
            check(down.position == lastFirstDown) {
                "Position from detectDrags() is not the same as position in trackDownPosition()"
            }
            check(pointersDownCount == 1) { "pointersDownCount is equal to $pointersDownCount" }
            check(pointersDown.size == 1 && pointersDown.keys.first() == down.id) {
                "pointersDown should only contain $down but it contains $pointersDown"
            }

            var overSlop = 0f
            val onTouchSlopReached = { change: PointerInputChange, over: Float ->
@@ -295,8 +303,9 @@ private class NestedDraggableNode(
                    }
                }

                check(pointersDownCount > 0) { "pointersDownCount is equal to $pointersDownCount" }
                val controller = draggable.onDragStarted(down.position, sign, pointersDownCount)
                check(pointersDown.size > 0) { "pointersDown is empty" }
                val controller =
                    draggable.onDragStarted(down.position, sign, pointersDown.size, drag.type)
                if (overSlop != 0f) {
                    onDrag(controller, drag, overSlop, velocityTracker)
                }
@@ -450,20 +459,24 @@ private class NestedDraggableNode(

    private suspend fun PointerInputScope.trackDownPosition() {
        awaitEachGesture {
            try {
                val down = awaitFirstDown(requireUnconsumed = false)
                lastFirstDown = down.position
            pointersDownCount = 1
                pointersDown[down.id] = down.type

                do {
                pointersDownCount +=
                    awaitPointerEvent().changes.fastSumBy { change ->
                    awaitPointerEvent().changes.forEach { change ->
                        when {
                            change.changedToDownIgnoreConsumed() -> 1
                            change.changedToUpIgnoreConsumed() -> -1
                            else -> 0
                            change.changedToDownIgnoreConsumed() -> {
                                pointersDown[change.id] = change.type
                            }
                            change.changedToUpIgnoreConsumed() -> pointersDown.remove(change.id)
                        }
                    }
            } while (pointersDownCount > 0)
                } while (pointersDown.size > 0)
            } finally {
                pointersDown.clear()
            }
        }
    }

@@ -491,12 +504,13 @@ private class NestedDraggableNode(
        if (nestedScrollController == null && draggable.shouldConsumeNestedScroll(sign)) {
            val startedPosition = checkNotNull(lastFirstDown) { "lastFirstDown is not set" }

            // TODO(b/382665591): Replace this by check(pointersDownCount > 0).
            val pointersDown = pointersDownCount.coerceAtLeast(1)
            // TODO(b/382665591): Ensure that there is at least one pointer down.
            val pointersDownCount = pointersDown.size.coerceAtLeast(1)
            val pointerType = pointersDown.entries.firstOrNull()?.value
            nestedScrollController =
                NestedScrollController(
                    overscrollEffect,
                    draggable.onDragStarted(startedPosition, sign, pointersDown),
                    draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType),
                )
        }

+60 −0
Original line number Diff line number Diff line
@@ -33,10 +33,12 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performMouseInput
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeDown
import androidx.compose.ui.test.swipeLeft
@@ -653,6 +655,61 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
        assertThat(flingIsDone).isTrue()
    }

    @Test
    fun pointerType() {
        val draggable = TestDraggable()
        val touchSlop =
            rule.setContentWithTouchSlop {
                Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
            }

        rule.onRoot().performTouchInput {
            down(center)
            moveBy(touchSlop.toOffset())
        }

        assertThat(draggable.onDragStartedPointerType).isEqualTo(PointerType.Touch)
    }

    @Test
    fun pointerType_mouse() {
        val draggable = TestDraggable()
        val touchSlop =
            rule.setContentWithTouchSlop {
                Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
            }

        rule.onRoot().performMouseInput {
            moveTo(center)
            press()
            moveBy(touchSlop.toOffset())
            release()
        }

        assertThat(draggable.onDragStartedPointerType).isEqualTo(PointerType.Mouse)
    }

    @Test
    fun pointersDown_clearedWhenDisabled() {
        val draggable = TestDraggable()
        var enabled by mutableStateOf(true)
        rule.setContent {
            Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation, enabled = enabled))
        }

        rule.onRoot().performTouchInput { down(center) }

        enabled = false
        rule.waitForIdle()

        rule.onRoot().performTouchInput { up() }

        enabled = true
        rule.waitForIdle()

        rule.onRoot().performTouchInput { down(center) }
    }

    private fun ComposeContentTestRule.setContentWithTouchSlop(
        content: @Composable () -> Unit
    ): Float {
@@ -688,6 +745,7 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
        var onDragStartedPosition = Offset.Zero
        var onDragStartedSign = 0f
        var onDragStartedPointersDown = 0
        var onDragStartedPointerType: PointerType? = null
        var onDragDelta = 0f

        override fun shouldStartDrag(change: PointerInputChange): Boolean = shouldStartDrag
@@ -696,11 +754,13 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
            position: Offset,
            sign: Float,
            pointersDown: Int,
            pointerType: PointerType?,
        ): NestedDraggable.Controller {
            onDragStartedCalled = true
            onDragStartedPosition = position
            onDragStartedSign = sign
            onDragStartedPointersDown = pointersDown
            onDragStartedPointerType = pointerType
            onDragDelta = 0f

            onDragStarted.invoke(position, sign)