diff options
-rw-r--r-- | packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt | 46 | ||||
-rw-r--r-- | packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt | 39 |
2 files changed, 67 insertions, 18 deletions
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt index e02e8b483543..061521aa3244 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt @@ -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) } @@ -452,18 +461,18 @@ private class NestedDraggableNode( awaitEachGesture { val down = awaitFirstDown(requireUnconsumed = false) lastFirstDown = down.position - pointersDownCount = 1 + pointersDown[down.id] = down.type do { - pointersDownCount += - awaitPointerEvent().changes.fastSumBy { change -> - when { - change.changedToDownIgnoreConsumed() -> 1 - change.changedToUpIgnoreConsumed() -> -1 - else -> 0 + awaitPointerEvent().changes.forEach { change -> + when { + change.changedToDownIgnoreConsumed() -> { + pointersDown[change.id] = change.type } + change.changedToUpIgnoreConsumed() -> pointersDown.remove(change.id) } - } while (pointersDownCount > 0) + } + } while (pointersDown.size > 0) } } @@ -491,12 +500,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), ) } diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt index 9c49090916e3..896ebf6dc82b 100644 --- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt +++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt @@ -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,40 @@ 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) + } + private fun ComposeContentTestRule.setContentWithTouchSlop( content: @Composable () -> Unit ): Float { @@ -688,6 +724,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 +733,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) |