Loading packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt +33 −16 Original line number Original line Diff line number Diff line Loading @@ -137,9 +137,18 @@ fun Modifier.nestedDraggable( orientation: Orientation, orientation: Orientation, overscrollEffect: OverscrollEffect? = null, overscrollEffect: OverscrollEffect? = null, enabled: Boolean = true, enabled: Boolean = true, nestedDragsEnabled: Boolean = true, ): Modifier { ): Modifier { return this.thenIf(overscrollEffect != null) { Modifier.overscroll(overscrollEffect) } return this.thenIf(overscrollEffect != null) { Modifier.overscroll(overscrollEffect) } .then(NestedDraggableElement(draggable, orientation, overscrollEffect, enabled)) .then( NestedDraggableElement( draggable, orientation, overscrollEffect, enabled, nestedDragsEnabled, ) ) } } private data class NestedDraggableElement( private data class NestedDraggableElement( Loading @@ -147,13 +156,20 @@ private data class NestedDraggableElement( private val orientation: Orientation, private val orientation: Orientation, private val overscrollEffect: OverscrollEffect?, private val overscrollEffect: OverscrollEffect?, private val enabled: Boolean, private val enabled: Boolean, private val nestedDragsEnabled: Boolean, ) : ModifierNodeElement<NestedDraggableRootNode>() { ) : ModifierNodeElement<NestedDraggableRootNode>() { override fun create(): NestedDraggableRootNode { override fun create(): NestedDraggableRootNode { return NestedDraggableRootNode(draggable, orientation, overscrollEffect, enabled) return NestedDraggableRootNode( draggable, orientation, overscrollEffect, enabled, nestedDragsEnabled, ) } } override fun update(node: NestedDraggableRootNode) { override fun update(node: NestedDraggableRootNode) { node.update(draggable, orientation, overscrollEffect, enabled) node.update(draggable, orientation, overscrollEffect, enabled, nestedDragsEnabled) } } } } Loading @@ -166,15 +182,17 @@ private class NestedDraggableRootNode( orientation: Orientation, orientation: Orientation, overscrollEffect: OverscrollEffect?, overscrollEffect: OverscrollEffect?, enabled: Boolean, enabled: Boolean, nestedDragsEnabled: Boolean, ) : DelegatingNode() { ) : DelegatingNode() { private var delegateNode = private var delegateNode = if (enabled) create(draggable, orientation, overscrollEffect) else null if (enabled) create(draggable, orientation, overscrollEffect, nestedDragsEnabled) else null fun update( fun update( draggable: NestedDraggable, draggable: NestedDraggable, orientation: Orientation, orientation: Orientation, overscrollEffect: OverscrollEffect?, overscrollEffect: OverscrollEffect?, enabled: Boolean, enabled: Boolean, nestedDragsEnabled: Boolean, ) { ) { // Disabled. // Disabled. if (!enabled) { if (!enabled) { Loading @@ -186,20 +204,23 @@ private class NestedDraggableRootNode( // Disabled => Enabled. // Disabled => Enabled. val nullableDelegate = delegateNode val nullableDelegate = delegateNode if (nullableDelegate == null) { if (nullableDelegate == null) { delegateNode = create(draggable, orientation, overscrollEffect) delegateNode = create(draggable, orientation, overscrollEffect, nestedDragsEnabled) return return } } // Enabled => Enabled (update). // Enabled => Enabled (update). nullableDelegate.update(draggable, orientation, overscrollEffect) nullableDelegate.update(draggable, orientation, overscrollEffect, nestedDragsEnabled) } } private fun create( private fun create( draggable: NestedDraggable, draggable: NestedDraggable, orientation: Orientation, orientation: Orientation, overscrollEffect: OverscrollEffect?, overscrollEffect: OverscrollEffect?, nestedDragsEnabled: Boolean, ): NestedDraggableNode { ): NestedDraggableNode { return delegate(NestedDraggableNode(draggable, orientation, overscrollEffect)) return delegate( NestedDraggableNode(draggable, orientation, overscrollEffect, nestedDragsEnabled) ) } } } } Loading @@ -207,6 +228,7 @@ private class NestedDraggableNode( private var draggable: NestedDraggable, private var draggable: NestedDraggable, override var orientation: Orientation, override var orientation: Orientation, private var overscrollEffect: OverscrollEffect?, private var overscrollEffect: OverscrollEffect?, private var nestedDragsEnabled: Boolean, ) : ) : DelegatingNode(), DelegatingNode(), PointerInputModifierNode, PointerInputModifierNode, Loading Loading @@ -247,18 +269,12 @@ private class NestedDraggableNode( draggable: NestedDraggable, draggable: NestedDraggable, orientation: Orientation, orientation: Orientation, overscrollEffect: OverscrollEffect?, overscrollEffect: OverscrollEffect?, nestedDragsEnabled: Boolean, ) { ) { if ( draggable == this.draggable && orientation == this.orientation && overscrollEffect == this.overscrollEffect ) { return } this.draggable = draggable this.draggable = draggable this.orientation = orientation this.orientation = orientation this.overscrollEffect = overscrollEffect this.overscrollEffect = overscrollEffect this.nestedDragsEnabled = nestedDragsEnabled trackWheelScroll.resetPointerInputHandler() trackWheelScroll.resetPointerInputHandler() trackDownPositionDelegate.resetPointerInputHandler() trackDownPositionDelegate.resetPointerInputHandler() Loading Loading @@ -545,6 +561,7 @@ private class NestedDraggableNode( val sign = offset.sign val sign = offset.sign if ( if ( nestedDragsEnabled && nestedScrollController == null && nestedScrollController == null && // TODO(b/388231324): Remove this. // TODO(b/388231324): Remove this. !lastEventWasScrollWheel && !lastEventWasScrollWheel && Loading packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt +21 −0 Original line number Original line Diff line number Diff line Loading @@ -172,6 +172,27 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw assertThat(effect.applyToFlingDone).isTrue() assertThat(effect.applyToFlingDone).isTrue() } } @Test fun nestedScrollable_disabled() { val draggable = TestDraggable() val effect = TestOverscrollEffect(orientation) { 0f } val touchSlop = rule.setContentWithTouchSlop { Box( Modifier.fillMaxSize() .nestedDraggable(draggable, orientation, effect, nestedDragsEnabled = false) .nestedScrollable(rememberScrollState()) ) } rule.onRoot().performTouchInput { down(center) moveBy((-touchSlop - 10f).toOffset()) } assertThat(draggable.onDragStartedCalled).isFalse() } @Test @Test fun onDragStoppedIsCalledWhenDraggableIsUpdatedAndReset() { fun onDragStoppedIsCalledWhenDraggableIsUpdatedAndReset() { val draggable = TestDraggable() val draggable = TestDraggable() Loading packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +0 −4 Original line number Original line Diff line number Diff line Loading @@ -458,10 +458,6 @@ internal class Swipes(val upOrLeft: Swipe.Resolved, val downOrRight: Swipe.Resol * @return The best matching [UserActionResult], or `null` if no match is found. * @return The best matching [UserActionResult], or `null` if no match is found. */ */ private fun Content.findActionResultBestMatch(swipe: Swipe.Resolved): UserActionResult? { private fun Content.findActionResultBestMatch(swipe: Swipe.Resolved): UserActionResult? { if (!areSwipesAllowed()) { return null } var bestPoints = Int.MIN_VALUE var bestPoints = Int.MIN_VALUE var bestMatch: UserActionResult? = null var bestMatch: UserActionResult? = null userActions.forEach { (actionSwipe, actionResult) -> userActions.forEach { (actionSwipe, actionResult) -> Loading packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +7 −4 Original line number Original line Diff line number Diff line Loading @@ -27,16 +27,19 @@ import com.android.compose.gesture.nestedDraggable */ */ @Stable @Stable internal fun Modifier.swipeToScene(draggableHandler: DraggableHandler): Modifier { internal fun Modifier.swipeToScene(draggableHandler: DraggableHandler): Modifier { val contentForSwipes = draggableHandler.contentForSwipes() val enabled = draggableHandler.enabled(contentForSwipes) return this.nestedDraggable( return this.nestedDraggable( draggable = draggableHandler, draggable = draggableHandler, orientation = draggableHandler.orientation, orientation = draggableHandler.orientation, overscrollEffect = draggableHandler.overscrollEffect, overscrollEffect = draggableHandler.overscrollEffect, enabled = draggableHandler.enabled(), enabled = enabled, nestedDragsEnabled = enabled && contentForSwipes.areNestedSwipesAllowed(), ) ) } } internal fun DraggableHandler.enabled(): Boolean { internal fun DraggableHandler.enabled(contentForSwipes: Content = contentForSwipes()): Boolean { return isDrivingTransition || contentForSwipes().shouldEnableSwipes(orientation) return isDrivingTransition || contentForSwipes.shouldEnableSwipes(orientation) } } private fun DraggableHandler.contentForSwipes(): Content { private fun DraggableHandler.contentForSwipes(): Content { Loading @@ -45,7 +48,7 @@ private fun DraggableHandler.contentForSwipes(): Content { /** Whether swipe should be enabled in the given [orientation]. */ /** Whether swipe should be enabled in the given [orientation]. */ private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean { private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean { if (userActions.isEmpty() || !areSwipesAllowed()) { if (userActions.isEmpty()) { return false return false } } Loading packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt +1 −1 Original line number Original line Diff line number Diff line Loading @@ -179,7 +179,7 @@ internal sealed class Content( } } } } fun areSwipesAllowed(): Boolean = nestedScrollControlState.isOuterScrollAllowed fun areNestedSwipesAllowed(): Boolean = nestedScrollControlState.isOuterScrollAllowed fun maybeUpdateEffects(effectFactory: OverscrollFactory) { fun maybeUpdateEffects(effectFactory: OverscrollFactory) { if (effectFactory != lastFactory) { if (effectFactory != lastFactory) { Loading Loading
packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt +33 −16 Original line number Original line Diff line number Diff line Loading @@ -137,9 +137,18 @@ fun Modifier.nestedDraggable( orientation: Orientation, orientation: Orientation, overscrollEffect: OverscrollEffect? = null, overscrollEffect: OverscrollEffect? = null, enabled: Boolean = true, enabled: Boolean = true, nestedDragsEnabled: Boolean = true, ): Modifier { ): Modifier { return this.thenIf(overscrollEffect != null) { Modifier.overscroll(overscrollEffect) } return this.thenIf(overscrollEffect != null) { Modifier.overscroll(overscrollEffect) } .then(NestedDraggableElement(draggable, orientation, overscrollEffect, enabled)) .then( NestedDraggableElement( draggable, orientation, overscrollEffect, enabled, nestedDragsEnabled, ) ) } } private data class NestedDraggableElement( private data class NestedDraggableElement( Loading @@ -147,13 +156,20 @@ private data class NestedDraggableElement( private val orientation: Orientation, private val orientation: Orientation, private val overscrollEffect: OverscrollEffect?, private val overscrollEffect: OverscrollEffect?, private val enabled: Boolean, private val enabled: Boolean, private val nestedDragsEnabled: Boolean, ) : ModifierNodeElement<NestedDraggableRootNode>() { ) : ModifierNodeElement<NestedDraggableRootNode>() { override fun create(): NestedDraggableRootNode { override fun create(): NestedDraggableRootNode { return NestedDraggableRootNode(draggable, orientation, overscrollEffect, enabled) return NestedDraggableRootNode( draggable, orientation, overscrollEffect, enabled, nestedDragsEnabled, ) } } override fun update(node: NestedDraggableRootNode) { override fun update(node: NestedDraggableRootNode) { node.update(draggable, orientation, overscrollEffect, enabled) node.update(draggable, orientation, overscrollEffect, enabled, nestedDragsEnabled) } } } } Loading @@ -166,15 +182,17 @@ private class NestedDraggableRootNode( orientation: Orientation, orientation: Orientation, overscrollEffect: OverscrollEffect?, overscrollEffect: OverscrollEffect?, enabled: Boolean, enabled: Boolean, nestedDragsEnabled: Boolean, ) : DelegatingNode() { ) : DelegatingNode() { private var delegateNode = private var delegateNode = if (enabled) create(draggable, orientation, overscrollEffect) else null if (enabled) create(draggable, orientation, overscrollEffect, nestedDragsEnabled) else null fun update( fun update( draggable: NestedDraggable, draggable: NestedDraggable, orientation: Orientation, orientation: Orientation, overscrollEffect: OverscrollEffect?, overscrollEffect: OverscrollEffect?, enabled: Boolean, enabled: Boolean, nestedDragsEnabled: Boolean, ) { ) { // Disabled. // Disabled. if (!enabled) { if (!enabled) { Loading @@ -186,20 +204,23 @@ private class NestedDraggableRootNode( // Disabled => Enabled. // Disabled => Enabled. val nullableDelegate = delegateNode val nullableDelegate = delegateNode if (nullableDelegate == null) { if (nullableDelegate == null) { delegateNode = create(draggable, orientation, overscrollEffect) delegateNode = create(draggable, orientation, overscrollEffect, nestedDragsEnabled) return return } } // Enabled => Enabled (update). // Enabled => Enabled (update). nullableDelegate.update(draggable, orientation, overscrollEffect) nullableDelegate.update(draggable, orientation, overscrollEffect, nestedDragsEnabled) } } private fun create( private fun create( draggable: NestedDraggable, draggable: NestedDraggable, orientation: Orientation, orientation: Orientation, overscrollEffect: OverscrollEffect?, overscrollEffect: OverscrollEffect?, nestedDragsEnabled: Boolean, ): NestedDraggableNode { ): NestedDraggableNode { return delegate(NestedDraggableNode(draggable, orientation, overscrollEffect)) return delegate( NestedDraggableNode(draggable, orientation, overscrollEffect, nestedDragsEnabled) ) } } } } Loading @@ -207,6 +228,7 @@ private class NestedDraggableNode( private var draggable: NestedDraggable, private var draggable: NestedDraggable, override var orientation: Orientation, override var orientation: Orientation, private var overscrollEffect: OverscrollEffect?, private var overscrollEffect: OverscrollEffect?, private var nestedDragsEnabled: Boolean, ) : ) : DelegatingNode(), DelegatingNode(), PointerInputModifierNode, PointerInputModifierNode, Loading Loading @@ -247,18 +269,12 @@ private class NestedDraggableNode( draggable: NestedDraggable, draggable: NestedDraggable, orientation: Orientation, orientation: Orientation, overscrollEffect: OverscrollEffect?, overscrollEffect: OverscrollEffect?, nestedDragsEnabled: Boolean, ) { ) { if ( draggable == this.draggable && orientation == this.orientation && overscrollEffect == this.overscrollEffect ) { return } this.draggable = draggable this.draggable = draggable this.orientation = orientation this.orientation = orientation this.overscrollEffect = overscrollEffect this.overscrollEffect = overscrollEffect this.nestedDragsEnabled = nestedDragsEnabled trackWheelScroll.resetPointerInputHandler() trackWheelScroll.resetPointerInputHandler() trackDownPositionDelegate.resetPointerInputHandler() trackDownPositionDelegate.resetPointerInputHandler() Loading Loading @@ -545,6 +561,7 @@ private class NestedDraggableNode( val sign = offset.sign val sign = offset.sign if ( if ( nestedDragsEnabled && nestedScrollController == null && nestedScrollController == null && // TODO(b/388231324): Remove this. // TODO(b/388231324): Remove this. !lastEventWasScrollWheel && !lastEventWasScrollWheel && Loading
packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt +21 −0 Original line number Original line Diff line number Diff line Loading @@ -172,6 +172,27 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw assertThat(effect.applyToFlingDone).isTrue() assertThat(effect.applyToFlingDone).isTrue() } } @Test fun nestedScrollable_disabled() { val draggable = TestDraggable() val effect = TestOverscrollEffect(orientation) { 0f } val touchSlop = rule.setContentWithTouchSlop { Box( Modifier.fillMaxSize() .nestedDraggable(draggable, orientation, effect, nestedDragsEnabled = false) .nestedScrollable(rememberScrollState()) ) } rule.onRoot().performTouchInput { down(center) moveBy((-touchSlop - 10f).toOffset()) } assertThat(draggable.onDragStartedCalled).isFalse() } @Test @Test fun onDragStoppedIsCalledWhenDraggableIsUpdatedAndReset() { fun onDragStoppedIsCalledWhenDraggableIsUpdatedAndReset() { val draggable = TestDraggable() val draggable = TestDraggable() Loading
packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +0 −4 Original line number Original line Diff line number Diff line Loading @@ -458,10 +458,6 @@ internal class Swipes(val upOrLeft: Swipe.Resolved, val downOrRight: Swipe.Resol * @return The best matching [UserActionResult], or `null` if no match is found. * @return The best matching [UserActionResult], or `null` if no match is found. */ */ private fun Content.findActionResultBestMatch(swipe: Swipe.Resolved): UserActionResult? { private fun Content.findActionResultBestMatch(swipe: Swipe.Resolved): UserActionResult? { if (!areSwipesAllowed()) { return null } var bestPoints = Int.MIN_VALUE var bestPoints = Int.MIN_VALUE var bestMatch: UserActionResult? = null var bestMatch: UserActionResult? = null userActions.forEach { (actionSwipe, actionResult) -> userActions.forEach { (actionSwipe, actionResult) -> Loading
packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +7 −4 Original line number Original line Diff line number Diff line Loading @@ -27,16 +27,19 @@ import com.android.compose.gesture.nestedDraggable */ */ @Stable @Stable internal fun Modifier.swipeToScene(draggableHandler: DraggableHandler): Modifier { internal fun Modifier.swipeToScene(draggableHandler: DraggableHandler): Modifier { val contentForSwipes = draggableHandler.contentForSwipes() val enabled = draggableHandler.enabled(contentForSwipes) return this.nestedDraggable( return this.nestedDraggable( draggable = draggableHandler, draggable = draggableHandler, orientation = draggableHandler.orientation, orientation = draggableHandler.orientation, overscrollEffect = draggableHandler.overscrollEffect, overscrollEffect = draggableHandler.overscrollEffect, enabled = draggableHandler.enabled(), enabled = enabled, nestedDragsEnabled = enabled && contentForSwipes.areNestedSwipesAllowed(), ) ) } } internal fun DraggableHandler.enabled(): Boolean { internal fun DraggableHandler.enabled(contentForSwipes: Content = contentForSwipes()): Boolean { return isDrivingTransition || contentForSwipes().shouldEnableSwipes(orientation) return isDrivingTransition || contentForSwipes.shouldEnableSwipes(orientation) } } private fun DraggableHandler.contentForSwipes(): Content { private fun DraggableHandler.contentForSwipes(): Content { Loading @@ -45,7 +48,7 @@ private fun DraggableHandler.contentForSwipes(): Content { /** Whether swipe should be enabled in the given [orientation]. */ /** Whether swipe should be enabled in the given [orientation]. */ private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean { private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean { if (userActions.isEmpty() || !areSwipesAllowed()) { if (userActions.isEmpty()) { return false return false } } Loading
packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt +1 −1 Original line number Original line Diff line number Diff line Loading @@ -179,7 +179,7 @@ internal sealed class Content( } } } } fun areSwipesAllowed(): Boolean = nestedScrollControlState.isOuterScrollAllowed fun areNestedSwipesAllowed(): Boolean = nestedScrollControlState.isOuterScrollAllowed fun maybeUpdateEffects(effectFactory: OverscrollFactory) { fun maybeUpdateEffects(effectFactory: OverscrollFactory) { if (effectFactory != lastFactory) { if (effectFactory != lastFactory) { Loading