Add enabled parameter to Modifier.nestedDraggable()

Bug: 378470603
Test: atest NestedDraggableTest
Flag: EXEMPT new API not used anywhere yet
Change-Id: I975c6eac3951880e2c3196a0e0baad2677501a73
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 58b8836..9fe85b7 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,22 +111,24 @@
     draggable: NestedDraggable,
     orientation: Orientation,
     overscrollEffect: OverscrollEffect? = null,
+    enabled: Boolean = true,
 ): Modifier {
     return this.thenIf(overscrollEffect != null) { Modifier.overscroll(overscrollEffect) }
-        .then(NestedDraggableElement(draggable, orientation, overscrollEffect))
+        .then(NestedDraggableElement(draggable, orientation, overscrollEffect, enabled))
 }
 
 private data class NestedDraggableElement(
     private val draggable: NestedDraggable,
     private val orientation: Orientation,
     private val overscrollEffect: OverscrollEffect?,
+    private val enabled: Boolean,
 ) : ModifierNodeElement<NestedDraggableNode>() {
     override fun create(): NestedDraggableNode {
-        return NestedDraggableNode(draggable, orientation, overscrollEffect)
+        return NestedDraggableNode(draggable, orientation, overscrollEffect, enabled)
     }
 
     override fun update(node: NestedDraggableNode) {
-        node.update(draggable, orientation, overscrollEffect)
+        node.update(draggable, orientation, overscrollEffect, enabled)
     }
 }
 
@@ -134,6 +136,7 @@
     private var draggable: NestedDraggable,
     override var orientation: Orientation,
     private var overscrollEffect: OverscrollEffect?,
+    private var enabled: Boolean,
 ) :
     DelegatingNode(),
     PointerInputModifierNode,
@@ -179,14 +182,22 @@
         draggable: NestedDraggable,
         orientation: Orientation,
         overscrollEffect: OverscrollEffect?,
+        enabled: Boolean,
     ) {
         this.draggable = draggable
         this.orientation = orientation
         this.overscrollEffect = overscrollEffect
+        this.enabled = enabled
 
         trackDownPositionDelegate?.resetPointerInputHandler()
         detectDragsDelegate?.resetPointerInputHandler()
         nestedScrollController?.ensureOnDragStoppedIsCalled()
+
+        if (!enabled && trackDownPositionDelegate != null) {
+            check(detectDragsDelegate != null)
+            trackDownPositionDelegate = null
+            detectDragsDelegate = null
+        }
     }
 
     override fun onPointerEvent(
@@ -194,6 +205,8 @@
         pass: PointerEventPass,
         bounds: IntSize,
     ) {
+        if (!enabled) return
+
         if (trackDownPositionDelegate == null) {
             check(detectDragsDelegate == null)
             trackDownPositionDelegate = SuspendingPointerInputModifierNode { trackDownPosition() }
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 f8561b8..fd3902f 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
@@ -344,6 +344,45 @@
         assertThat(draggable.onDragStoppedCalled).isTrue()
     }
 
+    @Test
+    fun enabled() {
+        val draggable = TestDraggable()
+        var enabled by mutableStateOf(false)
+        val touchSlop =
+            rule.setContentWithTouchSlop {
+                Box(
+                    Modifier.fillMaxSize()
+                        .nestedDraggable(draggable, orientation, enabled = enabled)
+                )
+            }
+
+        assertThat(draggable.onDragStartedCalled).isFalse()
+
+        rule.onRoot().performTouchInput {
+            down(center)
+            moveBy(touchSlop.toOffset())
+        }
+
+        assertThat(draggable.onDragStartedCalled).isFalse()
+        assertThat(draggable.onDragStoppedCalled).isFalse()
+
+        enabled = true
+        rule.onRoot().performTouchInput {
+            // Release previously up finger.
+            up()
+
+            down(center)
+            moveBy(touchSlop.toOffset())
+        }
+
+        assertThat(draggable.onDragStartedCalled).isTrue()
+        assertThat(draggable.onDragStoppedCalled).isFalse()
+
+        enabled = false
+        rule.waitForIdle()
+        assertThat(draggable.onDragStoppedCalled).isTrue()
+    }
+
     private fun ComposeContentTestRule.setContentWithTouchSlop(
         content: @Composable () -> Unit
     ): Float {