From f4a86dd4e4be54c6552fc76990dd7d50d463fe06 Mon Sep 17 00:00:00 2001 From: Jordan Demeulenaere Date: Tue, 11 Mar 2025 15:10:26 +0100 Subject: Add NestedDraggable.Controller.autoStopNestedDrags This CL adds a new autoStopNestedDrags flag to NestedDraggable.Controller to automatically stop a nested drag as soon as it does not consume some amount of scroll. This will be used by the media player to implement the "swipe to gear icon" behavior. Bug: 397989775 Test: atest NestedDraggableTest Flag: EXEMPT new unused API Change-Id: If4441298d18a8c7fa9e4a84bdfb3e86a5dc426a1 --- .../com/android/compose/gesture/NestedDraggable.kt | 19 ++++++++++-- .../android/compose/gesture/NestedDraggableTest.kt | 36 ++++++++++++++++++++++ 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 @@ -111,6 +111,14 @@ interface NestedDraggable { fun shouldConsumeNestedPreScroll(sign: Float): Boolean = false 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. * @@ -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 -- cgit v1.2.3-59-g8ed1b