summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt19
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt36
2 files changed, 53 insertions, 2 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 80cbbea7659f..2ea9c487c27c 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
@@ -112,6 +112,14 @@ interface NestedDraggable {
interface Controller {
/**
+ * Whether drags that were started from nested scrolls should be automatically
+ * [stopped][onDragStopped] as soon as they don't consume the entire `delta` passed to
+ * [onDrag].
+ */
+ val autoStopNestedDrags: Boolean
+ get() = false
+
+ /**
* Drag by [delta] pixels.
*
* @return the consumed [delta]. Any non-consumed delta will be dispatched to the next
@@ -609,8 +617,15 @@ private class NestedDraggableNode(
}
private fun scrollWithOverscroll(controller: NestedScrollController, offset: Offset): Offset {
- return scrollWithOverscroll(offset) {
- controller.controller.onDrag(it.toFloat()).toOffset()
+ return scrollWithOverscroll(offset) { delta ->
+ val available = delta.toFloat()
+ val consumed = controller.controller.onDrag(available)
+ if (controller.controller.autoStopNestedDrags && consumed != available) {
+ controller.ensureOnDragStoppedIsCalled()
+ this.nestedScrollController = null
+ }
+
+ consumed.toOffset()
}
}
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 a03d769c9c36..b247993de4e4 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
@@ -1000,6 +1000,39 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
assertThat(draggable.onDragStartedCalled).isTrue()
}
+ @Test
+ fun autoStopNestedDrags() {
+ var consumeScrolls by mutableStateOf(true)
+ val draggable =
+ TestDraggable(autoStopNestedDrags = true, onDrag = { if (consumeScrolls) it else 0f })
+
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ .scrollable(rememberScrollableState { 0f }, orientation)
+ )
+ }
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + 1f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ rule.onRoot().performTouchInput { moveBy(50f.toOffset()) }
+
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ consumeScrolls = false
+ rule.onRoot().performTouchInput { moveBy(1f.toOffset()) }
+
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
private fun ComposeContentTestRule.setContentWithTouchSlop(
content: @Composable () -> Unit
): Float {
@@ -1027,6 +1060,7 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
},
private val shouldConsumeNestedPostScroll: (Float) -> Boolean = { true },
private val shouldConsumeNestedPreScroll: (Float) -> Boolean = { false },
+ private val autoStopNestedDrags: Boolean = false,
) : NestedDraggable {
var shouldStartDrag = true
var onDragStartedCalled = false
@@ -1056,6 +1090,8 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
onDragStarted.invoke(position, sign)
return object : NestedDraggable.Controller {
+ override val autoStopNestedDrags: Boolean = this@TestDraggable.autoStopNestedDrags
+
override fun onDrag(delta: Float): Float {
onDragCalled = true
onDragDelta += delta