diff options
5 files changed, 26 insertions, 0 deletions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt index f38a31026664..9891025ad7d3 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt @@ -123,6 +123,10 @@ internal class DraggableHandlerImpl( overSlop: Float, pointersDown: Int, ): DragController { + if (startedPosition != null && layoutImpl.gestureFilter(startedPosition)) { + return NoOpDragController + } + if (overSlop == 0f) { val oldDragController = dragController check(oldDragController != null && oldDragController.isDrivingTransition) { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index cec888380513..6e89814a2dc2 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -47,6 +47,9 @@ import androidx.compose.ui.unit.LayoutDirection * @param state the state of this layout. * @param swipeSourceDetector the edge detector used to detect which edge a swipe is started from, * if any. + * @param gestureFilter decides whether a drag gesture that started at the given start position + * should be filtered. If the lambda returns `true`, the drag gesture will be ignored. If it + * returns `false`, the drag gesture will be handled. * @param transitionInterceptionThreshold used during a scene transition. For the scene to be * intercepted, the progress value must be above the threshold, and below (1 - threshold). * @param builder the configuration of the different scenes and overlays of this layout. @@ -57,6 +60,7 @@ fun SceneTransitionLayout( modifier: Modifier = Modifier, swipeSourceDetector: SwipeSourceDetector = DefaultEdgeDetector, swipeDetector: SwipeDetector = DefaultSwipeDetector, + gestureFilter: (startedPosition: Offset) -> Boolean = DefaultGestureFilter, @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0.05f, builder: SceneTransitionLayoutScope.() -> Unit, ) { @@ -65,6 +69,7 @@ fun SceneTransitionLayout( modifier, swipeSourceDetector, swipeDetector, + gestureFilter, transitionInterceptionThreshold, onLayoutImpl = null, builder, @@ -616,6 +621,7 @@ internal fun SceneTransitionLayoutForTesting( modifier: Modifier = Modifier, swipeSourceDetector: SwipeSourceDetector = DefaultEdgeDetector, swipeDetector: SwipeDetector = DefaultSwipeDetector, + gestureFilter: (startedPosition: Offset) -> Boolean = DefaultGestureFilter, transitionInterceptionThreshold: Float = 0f, onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)? = null, builder: SceneTransitionLayoutScope.() -> Unit, @@ -632,6 +638,7 @@ internal fun SceneTransitionLayoutForTesting( transitionInterceptionThreshold = transitionInterceptionThreshold, builder = builder, animationScope = animationScope, + gestureFilter = gestureFilter, ) .also { onLayoutImpl?.invoke(it) } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index 65c404387734..9e7be37523f4 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -31,6 +31,7 @@ import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.layout.ApproachLayoutModifierNode import androidx.compose.ui.layout.ApproachMeasureScope import androidx.compose.ui.layout.LookaheadScope @@ -70,6 +71,7 @@ internal class SceneTransitionLayoutImpl( * animations. */ internal val animationScope: CoroutineScope, + internal val gestureFilter: (startedPosition: Offset) -> Boolean, ) { /** * The map of [Scene]s. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeDetector.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeDetector.kt index 54ee78366875..f758102fee47 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeDetector.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeDetector.kt @@ -17,6 +17,7 @@ package com.android.compose.animation.scene import androidx.compose.runtime.Stable +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.PointerInputChange /** {@link SwipeDetector} helps determine whether a swipe gestured has occurred. */ @@ -31,6 +32,8 @@ interface SwipeDetector { val DefaultSwipeDetector = PassthroughSwipeDetector() +val DefaultGestureFilter = { _: Offset -> false } + /** An {@link SwipeDetector} implementation that recognizes a swipe on any input. */ class PassthroughSwipeDetector : SwipeDetector { override fun detectSwipe(change: PointerInputChange): Boolean { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt index fca92ca804fa..b64b8be90ad2 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt @@ -108,6 +108,8 @@ class DraggableHandlerTest { val transitionInterceptionThreshold = 0.05f + var gestureFilter: (startedPosition: Offset) -> Boolean = DefaultGestureFilter + private val layoutImpl = SceneTransitionLayoutImpl( state = layoutState, @@ -120,6 +122,7 @@ class DraggableHandlerTest { // Use testScope and not backgroundScope here because backgroundScope does not // work well with advanceUntilIdle(), which is used by some tests. animationScope = testScope, + gestureFilter = { startedPosition -> gestureFilter.invoke(startedPosition) }, ) .apply { setContentsAndLayoutTargetSizeForTest(LAYOUT_SIZE) } @@ -317,6 +320,13 @@ class DraggableHandlerTest { } @Test + fun onDragStarted_doesNotStartTransition_whenGestureFiltered() = runGestureTest { + gestureFilter = { _ -> true } + onDragStarted(overSlop = down(fractionOfScreen = 0.1f), expectedConsumedOverSlop = 0f) + assertIdle(currentScene = SceneA) + } + + @Test fun afterSceneTransitionIsStarted_interceptDragEvents() = runGestureTest { val dragController = onDragStarted(overSlop = down(fractionOfScreen = 0.1f)) assertTransition(currentScene = SceneA) |