Merge "Remove interception capabilities in STL drag handler" into main
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 cf0ba51..7a8d20a 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
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-@file:Suppress("NOTHING_TO_INLINE")
-
package com.android.compose.animation.scene
import androidx.compose.foundation.gestures.Orientation
@@ -97,68 +95,11 @@
internal val positionalThreshold
get() = with(layoutImpl.density) { 56.dp.toPx() }
- /**
- * Whether we should immediately intercept a gesture.
- *
- * Note: if this returns true, then [onDragStarted] will be called with overSlop equal to 0f,
- * indicating that the transition should be intercepted.
- */
- internal fun shouldImmediatelyIntercept(pointersDown: PointersInfo.PointersDown?): Boolean {
- // We don't intercept the touch if we are not currently driving the transition.
- val dragController = dragController
- if (dragController?.isDrivingTransition != true) {
- return false
- }
-
- val swipeAnimation = dragController.swipeAnimation
-
- // Only intercept the current transition if one of the 2 swipes results is also a transition
- // between the same pair of contents.
- val swipes = computeSwipes(pointersDown)
- val fromContent = layoutImpl.content(swipeAnimation.currentContent)
- val (upOrLeft, downOrRight) = swipes.computeSwipesResults(fromContent)
- val currentScene = layoutImpl.state.currentScene
- val contentTransition = swipeAnimation.contentTransition
- return (upOrLeft != null &&
- contentTransition.isTransitioningBetween(
- fromContent.key,
- upOrLeft.toContent(currentScene),
- )) ||
- (downOrRight != null &&
- contentTransition.isTransitioningBetween(
- fromContent.key,
- downOrRight.toContent(currentScene),
- ))
- }
-
override fun onDragStarted(
pointersDown: PointersInfo.PointersDown?,
overSlop: Float,
): DragController {
- if (overSlop == 0f) {
- val oldDragController = dragController
- check(oldDragController != null && oldDragController.isDrivingTransition) {
- val isActive = oldDragController?.isDrivingTransition
- "onDragStarted(overSlop=0f) requires an active dragController, but was $isActive"
- }
-
- // This [transition] was already driving the animation: simply take over it.
- // Stop animating and start from the current offset.
- val oldSwipeAnimation = oldDragController.swipeAnimation
-
- // We need to recompute the swipe results since this is a new gesture, and the
- // fromScene.userActions may have changed.
- val swipes = oldDragController.swipes
- swipes.updateSwipesResults(
- fromContent = layoutImpl.content(oldSwipeAnimation.fromContent)
- )
-
- // A new gesture should always create a new SwipeAnimation. This way there cannot be
- // different gestures controlling the same transition.
- val swipeAnimation = createSwipeAnimation(oldSwipeAnimation)
- return updateDragController(swipes, swipeAnimation)
- }
-
+ check(overSlop != 0f)
val swipes = computeSwipes(pointersDown)
val fromContent = layoutImpl.contentForUserActions()
@@ -196,7 +137,7 @@
return createSwipeAnimation(layoutImpl, result, isUpOrLeft, orientation)
}
- internal fun resolveSwipeSource(startedPosition: Offset): SwipeSource.Resolved? {
+ private fun resolveSwipeSource(startedPosition: Offset): SwipeSource.Resolved? {
return layoutImpl.swipeSourceDetector.source(
layoutSize = layoutImpl.lastSize,
position = startedPosition.round(),
@@ -291,71 +232,25 @@
return 0f
}
- val toContent = swipeAnimation.toContent
val distance = swipeAnimation.distance()
val previousOffset = swipeAnimation.dragOffset
val desiredOffset = previousOffset + delta
+ val desiredProgress = swipeAnimation.computeProgress(desiredOffset)
- fun hasReachedToSceneUpOrLeft() =
- distance < 0 &&
- desiredOffset <= distance &&
- swipes.upOrLeftResult?.toContent(layoutState.currentScene) == toContent
+ // Note: the distance could be negative if fromContent is above or to the left of
+ // toContent.
+ val newOffset =
+ when {
+ distance == DistanceUnspecified ||
+ swipeAnimation.contentTransition.isWithinProgressRange(desiredProgress) ->
+ desiredOffset
+ distance > 0f -> desiredOffset.fastCoerceIn(0f, distance)
+ else -> desiredOffset.fastCoerceIn(distance, 0f)
+ }
- fun hasReachedToSceneDownOrRight() =
- distance > 0 &&
- desiredOffset >= distance &&
- swipes.downOrRightResult?.toContent(layoutState.currentScene) == toContent
+ val consumedDelta = newOffset - previousOffset
- // Considering accelerated swipe: Change fromContent in the case where the user quickly
- // swiped multiple times in the same direction to accelerate the transition from A => B then
- // B => C.
- //
- // TODO(b/290184746): the second drag needs to pass B to work. Add support for flinging
- // twice before B has been reached
- val hasReachedToContent =
- swipeAnimation.currentContent == toContent &&
- (hasReachedToSceneUpOrLeft() || hasReachedToSceneDownOrRight())
-
- val fromContent: ContentKey
- val currentTransitionOffset: Float
- val newOffset: Float
- val consumedDelta: Float
- if (hasReachedToContent) {
- // The new transition will start from the current toContent.
- fromContent = toContent
-
- // The current transition is completed (we have reached the full swipe distance).
- currentTransitionOffset = distance
-
- // The next transition will start with the remaining offset.
- newOffset = desiredOffset - distance
- consumedDelta = delta
- } else {
- fromContent = swipeAnimation.fromContent
- val desiredProgress = swipeAnimation.computeProgress(desiredOffset)
-
- // Note: the distance could be negative if fromContent is above or to the left of
- // toContent.
- currentTransitionOffset =
- when {
- distance == DistanceUnspecified ||
- swipeAnimation.contentTransition.isWithinProgressRange(desiredProgress) ->
- desiredOffset
-
- distance > 0f -> desiredOffset.fastCoerceIn(0f, distance)
- else -> desiredOffset.fastCoerceIn(distance, 0f)
- }
-
- // If there is a new transition, we will use the same offset
- newOffset = currentTransitionOffset
- consumedDelta = newOffset - previousOffset
- }
-
- swipeAnimation.dragOffset = currentTransitionOffset
-
- if (hasReachedToContent) {
- swipes.updateSwipesResults(draggableHandler.layoutImpl.content(fromContent))
- }
+ swipeAnimation.dragOffset = newOffset
val result = swipes.findUserActionResult(directionOffset = newOffset)
if (result == null) {
@@ -372,13 +267,12 @@
val needNewTransition =
!currentTransitionIrreversible &&
- (hasReachedToContent ||
- result.toContent(layoutState.currentScene) != swipeAnimation.toContent ||
+ (result.toContent(layoutState.currentScene) != swipeAnimation.toContent ||
result.transitionKey != swipeAnimation.contentTransition.key)
if (needNewTransition) {
// Make sure the current transition will finish to the right current scene.
- swipeAnimation.currentContent = fromContent
+ swipeAnimation.currentContent = swipeAnimation.fromContent
val newSwipeAnimation = draggableHandler.createSwipeAnimation(swipes, result)
newSwipeAnimation.dragOffset = newOffset
@@ -499,7 +393,9 @@
var upOrLeftResult: UserActionResult? = null
var downOrRightResult: UserActionResult? = null
- fun computeSwipesResults(fromContent: Content): Pair<UserActionResult?, UserActionResult?> {
+ private fun computeSwipesResults(
+ fromContent: Content
+ ): Pair<UserActionResult?, UserActionResult?> {
val upOrLeftResult = fromContent.findActionResultBestMatch(swipe = upOrLeft)
val downOrRightResult = fromContent.findActionResultBestMatch(swipe = downOrRight)
return upOrLeftResult to downOrRightResult
@@ -564,48 +460,9 @@
.shouldEnableSwipes(draggableHandler.orientation)
}
- var isIntercepting = false
-
return PriorityNestedScrollConnection(
orientation = draggableHandler.orientation,
- canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
- val pointersDown: PointersInfo.PointersDown? =
- when (val info = pointersInfoOwner.pointersInfo()) {
- PointersInfo.MouseWheel -> {
- // Do not support mouse wheel interactions
- return@PriorityNestedScrollConnection false
- }
-
- is PointersInfo.PointersDown -> info
- null -> null
- }
-
- canChangeScene =
- if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f
-
- val canInterceptSwipeTransition =
- canChangeScene &&
- offsetAvailable != 0f &&
- draggableHandler.shouldImmediatelyIntercept(pointersDown)
- if (!canInterceptSwipeTransition) return@PriorityNestedScrollConnection false
-
- val layoutImpl = draggableHandler.layoutImpl
- val threshold = layoutImpl.transitionInterceptionThreshold
- val hasSnappedToIdle = layoutImpl.state.snapToIdleIfClose(threshold)
- if (hasSnappedToIdle) {
- // If the current swipe transition is closed to 0f or 1f, then we want to
- // interrupt the transition (snapping it to Idle) and scroll the list.
- return@PriorityNestedScrollConnection false
- }
-
- lastPointersDown = pointersDown
-
- // If the current swipe transition is *not* closed to 0f or 1f, then we want the
- // scroll events to intercept the current transition to continue the scene
- // transition.
- isIntercepting = true
- true
- },
+ canStartPreScroll = { _, _, _ -> false },
canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ ->
val behavior: NestedScrollBehavior =
when {
@@ -629,29 +486,22 @@
}
lastPointersDown = pointersDown
- val canStart =
- when (behavior) {
- NestedScrollBehavior.EdgeNoPreview -> {
- canChangeScene = isZeroOffset
- isZeroOffset && shouldEnableSwipes()
- }
-
- NestedScrollBehavior.EdgeWithPreview -> {
- canChangeScene = isZeroOffset
- shouldEnableSwipes()
- }
-
- NestedScrollBehavior.EdgeAlways -> {
- canChangeScene = true
- shouldEnableSwipes()
- }
+ when (behavior) {
+ NestedScrollBehavior.EdgeNoPreview -> {
+ canChangeScene = isZeroOffset
+ isZeroOffset && shouldEnableSwipes()
}
- if (canStart) {
- isIntercepting = false
- }
+ NestedScrollBehavior.EdgeWithPreview -> {
+ canChangeScene = isZeroOffset
+ shouldEnableSwipes()
+ }
- canStart
+ NestedScrollBehavior.EdgeAlways -> {
+ canChangeScene = true
+ shouldEnableSwipes()
+ }
+ }
},
canStartPostFling = { velocityAvailable ->
val behavior: NestedScrollBehavior =
@@ -676,19 +526,14 @@
}
lastPointersDown = pointersDown
- val canStart = behavior.canStartOnPostFling && shouldEnableSwipes()
- if (canStart) {
- isIntercepting = false
- }
-
- canStart
+ behavior.canStartOnPostFling && shouldEnableSwipes()
},
onStart = { firstScroll ->
scrollController(
dragController =
draggableHandler.onDragStarted(
pointersDown = lastPointersDown,
- overSlop = if (isIntercepting) 0f else firstScroll,
+ overSlop = firstScroll,
),
canChangeScene = canChangeScene,
pointersInfoOwner = pointersInfoOwner,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index 501fbb0..f5f01d4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -80,7 +80,6 @@
@Stable
internal fun Modifier.multiPointerDraggable(
orientation: Orientation,
- startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean,
onDragStarted: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
onFirstPointerDown: () -> Unit = {},
swipeDetector: SwipeDetector = DefaultSwipeDetector,
@@ -89,7 +88,6 @@
this.then(
MultiPointerDraggableElement(
orientation,
- startDragImmediately,
onDragStarted,
onFirstPointerDown,
swipeDetector,
@@ -99,7 +97,6 @@
private data class MultiPointerDraggableElement(
private val orientation: Orientation,
- private val startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean,
private val onDragStarted:
(pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
private val onFirstPointerDown: () -> Unit,
@@ -109,7 +106,6 @@
override fun create(): MultiPointerDraggableNode =
MultiPointerDraggableNode(
orientation = orientation,
- startDragImmediately = startDragImmediately,
onDragStarted = onDragStarted,
onFirstPointerDown = onFirstPointerDown,
swipeDetector = swipeDetector,
@@ -118,7 +114,6 @@
override fun update(node: MultiPointerDraggableNode) {
node.orientation = orientation
- node.startDragImmediately = startDragImmediately
node.onDragStarted = onDragStarted
node.onFirstPointerDown = onFirstPointerDown
node.swipeDetector = swipeDetector
@@ -127,7 +122,6 @@
internal class MultiPointerDraggableNode(
orientation: Orientation,
- var startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean,
var onDragStarted: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
var onFirstPointerDown: () -> Unit,
swipeDetector: SwipeDetector = DefaultSwipeDetector,
@@ -293,7 +287,6 @@
try {
detectDragGestures(
orientation = orientation,
- startDragImmediately = startDragImmediately,
onDragStart = { pointersDown, overSlop ->
onDragStarted(pointersDown, overSlop)
},
@@ -434,13 +427,11 @@
* Detect drag gestures in the given [orientation].
*
* This function is a mix of [androidx.compose.foundation.gestures.awaitDownAndSlop] and
- * [androidx.compose.foundation.gestures.detectVerticalDragGestures] to add support for:
- * 1) starting the gesture immediately without requiring a drag >= touch slope;
- * 2) passing the number of pointers down to [onDragStart].
+ * [androidx.compose.foundation.gestures.detectVerticalDragGestures] to add support for passing
+ * the number of pointers down to [onDragStart].
*/
private suspend fun AwaitPointerEventScope.detectDragGestures(
orientation: Orientation,
- startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean,
onDragStart: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
onDrag: (controller: DragController, dragAmount: Float) -> Unit,
onDragEnd: (controller: DragController) -> Unit,
@@ -466,71 +457,49 @@
.first()
var overSlop = 0f
- var lastPointersDown: PointersInfo.PointersDown =
+ val onSlopReached = { change: PointerInputChange, over: Float ->
+ if (swipeDetector.detectSwipe(change)) {
+ change.consume()
+ overSlop = over
+ }
+ }
+
+ // TODO(b/291055080): Replace by await[Orientation]PointerSlopOrCancellation once it
+ // is public.
+ val drag =
+ when (orientation) {
+ Orientation.Horizontal ->
+ awaitHorizontalTouchSlopOrCancellation(consumablePointer.id, onSlopReached)
+ Orientation.Vertical ->
+ awaitVerticalTouchSlopOrCancellation(consumablePointer.id, onSlopReached)
+ } ?: return
+
+ val lastPointersDown =
checkNotNull(pointersInfo()) {
"We should have pointers down, last event: $currentEvent"
}
as PointersInfo.PointersDown
-
- val drag =
- if (startDragImmediately(lastPointersDown)) {
- consumablePointer.consume()
- consumablePointer
- } else {
- val onSlopReached = { change: PointerInputChange, over: Float ->
- if (swipeDetector.detectSwipe(change)) {
- change.consume()
- overSlop = over
- }
+ // Make sure that overSlop is not 0f. This can happen when the user drags by exactly
+ // the touch slop. However, the overSlop we pass to onDragStarted() is used to
+ // compute the direction we are dragging in, so overSlop should never be 0f.
+ if (overSlop == 0f) {
+ // If the user drags in the opposite direction, the delta becomes zero because
+ // we return to the original point. Therefore, we should use the previous event
+ // to calculate the direction.
+ val delta = (drag.position - drag.previousPosition).toFloat()
+ check(delta != 0f) {
+ buildString {
+ append("delta is equal to 0 ")
+ append("touchSlop ${currentValueOf(LocalViewConfiguration).touchSlop} ")
+ append("consumablePointer.position ${consumablePointer.position} ")
+ append("drag.position ${drag.position} ")
+ append("drag.previousPosition ${drag.previousPosition}")
}
-
- // TODO(b/291055080): Replace by await[Orientation]PointerSlopOrCancellation once it
- // is public.
- val drag =
- when (orientation) {
- Orientation.Horizontal ->
- awaitHorizontalTouchSlopOrCancellation(
- consumablePointer.id,
- onSlopReached,
- )
- Orientation.Vertical ->
- awaitVerticalTouchSlopOrCancellation(
- consumablePointer.id,
- onSlopReached,
- )
- } ?: return
-
- lastPointersDown =
- checkNotNull(pointersInfo()) {
- "We should have pointers down, last event: $currentEvent"
- }
- as PointersInfo.PointersDown
- // Make sure that overSlop is not 0f. This can happen when the user drags by exactly
- // the touch slop. However, the overSlop we pass to onDragStarted() is used to
- // compute the direction we are dragging in, so overSlop should never be 0f unless
- // we intercept an ongoing swipe transition (i.e. startDragImmediately() returned
- // true).
- if (overSlop == 0f) {
- // If the user drags in the opposite direction, the delta becomes zero because
- // we return to the original point. Therefore, we should use the previous event
- // to calculate the direction.
- val delta = (drag.position - drag.previousPosition).toFloat()
- check(delta != 0f) {
- buildString {
- append("delta is equal to 0 ")
- append("touchSlop ${currentValueOf(LocalViewConfiguration).touchSlop} ")
- append("consumablePointer.position ${consumablePointer.position} ")
- append("drag.position ${drag.position} ")
- append("drag.previousPosition ${drag.previousPosition}")
- }
- }
- overSlop = delta.sign
- }
- drag
}
+ overSlop = delta.sign
+ }
val controller = onDragStart(lastPointersDown, overSlop)
-
val successful: Boolean
try {
onDrag(controller, overSlop)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index 5ab306a..6ef8b86 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -149,7 +149,6 @@
delegate(
MultiPointerDraggableNode(
orientation = draggableHandler.orientation,
- startDragImmediately = ::startDragImmediately,
onDragStarted = draggableHandler::onDragStarted,
onFirstPointerDown = ::onFirstPointerDown,
swipeDetector = swipeDetector,
@@ -198,21 +197,6 @@
) = multiPointerDraggableNode.onPointerEvent(pointerEvent, pass, bounds)
override fun onCancelPointerInput() = multiPointerDraggableNode.onCancelPointerInput()
-
- private fun startDragImmediately(pointersDown: PointersInfo.PointersDown): Boolean {
- // Immediately start the drag if the user can't swipe in the other direction and the gesture
- // handler can intercept it.
- return !canOppositeSwipe() && draggableHandler.shouldImmediatelyIntercept(pointersDown)
- }
-
- private fun canOppositeSwipe(): Boolean {
- val oppositeOrientation =
- when (draggableHandler.orientation) {
- Orientation.Vertical -> Orientation.Horizontal
- Orientation.Horizontal -> Orientation.Vertical
- }
- return draggableHandler.contentForSwipes().shouldEnableSwipes(oppositeOrientation)
- }
}
/** Find the [ScrollBehaviorOwner] for the current orientation. */
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 a1077cf..394568d 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
@@ -234,12 +234,6 @@
)
}
- fun onDragStartedImmediately(
- pointersInfo: PointersInfo.PointersDown = pointersDown()
- ): DragController {
- return onDragStarted(draggableHandler, pointersInfo, overSlop = 0f)
- }
-
fun onDragStarted(
draggableHandler: DraggableHandler,
pointersInfo: PointersInfo.PointersDown = pointersDown(),
@@ -602,82 +596,6 @@
}
@Test
- fun onAcceleratedScroll_scrollToThirdScene() = runGestureTest {
- // Drag A -> B with progress 0.2
- val dragController1 = onDragStarted(overSlop = up(fractionOfScreen = 0.2f))
- assertTransition(
- currentScene = SceneA,
- fromScene = SceneA,
- toScene = SceneB,
- progress = 0.2f,
- )
-
- // Start animation A -> B with progress 0.2 -> 1.0
- dragController1.onDragStoppedAnimateLater(velocity = -velocityThreshold)
- assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB)
-
- // While at A -> B do a 100% screen drag (progress 1.2). This should go past B and change
- // the transition to B -> C with progress 0.2
- val dragController2 = onDragStartedImmediately()
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 1f))
- assertTransition(
- currentScene = SceneB,
- fromScene = SceneB,
- toScene = SceneC,
- progress = 0.2f,
- )
-
- // After the drag stopped scene C should be committed
- dragController2.onDragStoppedAnimateNow(
- velocity = -velocityThreshold,
- onAnimationStart = {
- assertTransition(currentScene = SceneC, fromScene = SceneB, toScene = SceneC)
- },
- expectedConsumedVelocity = -velocityThreshold,
- )
- assertIdle(currentScene = SceneC)
- }
-
- @Test
- fun onAcceleratedScrollBothTargetsBecomeNull_settlesToIdle() = runGestureTest {
- val dragController1 = onDragStarted(overSlop = up(fractionOfScreen = 0.2f))
- dragController1.onDragDelta(pixels = up(fractionOfScreen = 0.2f))
- dragController1.onDragStoppedAnimateLater(velocity = -velocityThreshold)
- assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB)
-
- mutableUserActionsA = emptyMap()
- mutableUserActionsB = emptyMap()
-
- // start acceleratedScroll and scroll over to B -> null
- val dragController2 = onDragStartedImmediately()
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
-
- // here onDragStopped is already triggered, but subsequent onDelta/onDragStopped calls may
- // still be called. Make sure that they don't crash or change the scene
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
- dragController2.onDragStoppedAnimateNow(
- velocity = 0f,
- onAnimationStart = {
- assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB)
- },
- expectedConsumedVelocity = 0f,
- )
-
- advanceUntilIdle()
- assertIdle(SceneB)
-
- // These events can still come in after the animation has settled
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
- dragController2.onDragStoppedAnimateNow(
- velocity = 0f,
- onAnimationStart = { assertIdle(SceneB) },
- expectedConsumedVelocity = 0f,
- )
- assertIdle(SceneB)
- }
-
- @Test
fun onDragTargetsChanged_targetStaysTheSame() = runGestureTest {
val dragController1 = onDragStarted(overSlop = up(fractionOfScreen = 0.1f))
assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.1f)
@@ -711,9 +629,8 @@
dragController1.onDragStoppedAnimateLater(velocity = down(fractionOfScreen = 0.1f))
// now target changed to C for new drag that started before previous drag settled to Idle
- val dragController2 = onDragStartedImmediately()
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.1f))
- assertTransition(fromScene = SceneA, toScene = SceneC, progress = 0.3f)
+ onDragStarted(overSlop = up(fractionOfScreen = 0.1f))
+ assertTransition(fromScene = SceneA, toScene = SceneC, progress = 0.1f)
}
@Test
@@ -728,7 +645,7 @@
assertThat(isUserInputOngoing).isFalse()
// Start a new gesture while the offset is animating
- onDragStartedImmediately()
+ onDragStarted(overSlop = up(fractionOfScreen = 0.1f))
assertThat(isUserInputOngoing).isTrue()
}
@@ -812,36 +729,6 @@
}
@Test
- fun scrollAndFling_scrollLessThanInterceptable_goToIdleOnCurrentScene() = runGestureTest {
- val firstScroll = (transitionInterceptionThreshold - 0.0001f) * SCREEN_SIZE
- val secondScroll = 1f
-
- preScrollAfterSceneTransition(firstScroll = firstScroll, secondScroll = secondScroll)
-
- assertIdle(SceneA)
- }
-
- @Test
- fun scrollAndFling_scrollMinInterceptable_interceptPreScrollEvents() = runGestureTest {
- val firstScroll = (transitionInterceptionThreshold + 0.0001f) * SCREEN_SIZE
- val secondScroll = 1f
-
- preScrollAfterSceneTransition(firstScroll = firstScroll, secondScroll = secondScroll)
-
- assertTransition(progress = (firstScroll + secondScroll) / SCREEN_SIZE)
- }
-
- @Test
- fun scrollAndFling_scrollMaxInterceptable_interceptPreScrollEvents() = runGestureTest {
- val firstScroll = -(1f - transitionInterceptionThreshold - 0.0001f) * SCREEN_SIZE
- val secondScroll = -1f
-
- preScrollAfterSceneTransition(firstScroll = firstScroll, secondScroll = secondScroll)
-
- assertTransition(progress = -(firstScroll + secondScroll) / SCREEN_SIZE)
- }
-
- @Test
fun scrollAndFling_scrollMoreThanInterceptable_goToIdleOnNextScene() = runGestureTest {
val firstScroll = -(1f - transitionInterceptionThreshold + 0.0001f) * SCREEN_SIZE
val secondScroll = -0.01f
@@ -1025,7 +912,7 @@
// now we can intercept the scroll events
nestedScroll.scroll(available = -offsetY10)
- assertThat(progress).isEqualTo(0.2f)
+ assertThat(progress).isEqualTo(0.1f)
// this should be ignored, we are scrolling now!
dragController.onDragStoppedAnimateNow(
@@ -1036,10 +923,10 @@
assertTransition(currentScene = SceneA)
nestedScroll.scroll(available = -offsetY10)
- assertThat(progress).isEqualTo(0.3f)
+ assertThat(progress).isEqualTo(0.2f)
nestedScroll.scroll(available = -offsetY10)
- assertThat(progress).isEqualTo(0.4f)
+ assertThat(progress).isEqualTo(0.3f)
nestedScroll.preFling(available = Velocity(0f, -velocityThreshold))
assertTransition(currentScene = SceneB)
@@ -1050,57 +937,6 @@
}
@Test
- fun interceptTransition() = runGestureTest {
- // Start at scene C.
- navigateToSceneC()
-
- // Swipe up from the middle to transition to scene B.
- val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
- onDragStarted(pointersInfo = middle, overSlop = up(0.1f))
- assertTransition(
- currentScene = SceneC,
- fromScene = SceneC,
- toScene = SceneB,
- progress = 0.1f,
- isUserInputOngoing = true,
- )
-
- val firstTransition = transitionState
-
- // During the current gesture, start a new gesture, still in the middle of the screen. We
- // should intercept it. Because it is intercepted, the overSlop passed to onDragStarted()
- // should be 0f.
- assertThat(draggableHandler.shouldImmediatelyIntercept(middle)).isTrue()
- onDragStartedImmediately(pointersInfo = middle)
-
- // We should have intercepted the transition, so the transition should be the same object.
- assertTransition(
- currentScene = SceneC,
- fromScene = SceneC,
- toScene = SceneB,
- progress = 0.1f,
- isUserInputOngoing = true,
- )
- // We should have a new transition
- assertThat(transitionState).isNotSameInstanceAs(firstTransition)
-
- // Start a new gesture from the bottom of the screen. Because swiping up from the bottom of
- // C leads to scene A (and not B), the previous transitions is *not* intercepted and we
- // instead animate from C to A.
- val bottom = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2, SCREEN_SIZE))
- assertThat(draggableHandler.shouldImmediatelyIntercept(bottom)).isFalse()
- onDragStarted(pointersInfo = bottom, overSlop = up(0.1f))
-
- assertTransition(
- currentScene = SceneC,
- fromScene = SceneC,
- toScene = SceneA,
- isUserInputOngoing = true,
- )
- assertThat(transitionState).isNotSameInstanceAs(firstTransition)
- }
-
- @Test
fun freezeAndAnimateToCurrentState() = runGestureTest {
// Start at scene C.
navigateToSceneC()
@@ -1110,9 +946,6 @@
onDragStarted(pointersInfo = middle, overSlop = up(0.1f))
assertTransition(fromScene = SceneC, toScene = SceneB, isUserInputOngoing = true)
- // The current transition can be intercepted.
- assertThat(draggableHandler.shouldImmediatelyIntercept(middle)).isTrue()
-
// Freeze the transition.
val transition = transitionState as Transition
transition.freezeAndAnimateToCurrentState()
@@ -1123,19 +956,6 @@
}
@Test
- fun interruptedTransitionCanNotBeImmediatelyIntercepted() = runGestureTest {
- assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isFalse()
- onDragStarted(overSlop = up(0.1f))
- assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isTrue()
-
- layoutState.startTransitionImmediately(
- animationScope = testScope.backgroundScope,
- transition(SceneA, SceneB),
- )
- assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isFalse()
- }
-
- @Test
fun blockTransition() = runGestureTest {
assertIdle(SceneA)
@@ -1154,30 +974,6 @@
}
@Test
- fun blockInterceptedTransition() = runGestureTest {
- assertIdle(SceneA)
-
- // Swipe up to B.
- val dragController1 = onDragStarted(overSlop = up(0.1f))
- assertTransition(currentScene = SceneA, fromScene = SceneA, toScene = SceneB)
- dragController1.onDragStoppedAnimateLater(velocity = -velocityThreshold)
- assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB)
-
- // Intercept the transition and swipe down back to scene A.
- assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isTrue()
- val dragController2 = onDragStartedImmediately()
-
- // Block the transition when the user release their finger.
- canChangeScene = { false }
- dragController2.onDragStoppedAnimateNow(
- velocity = velocityThreshold,
- onAnimationStart = { assertTransition(fromScene = SceneA, toScene = SceneB) },
- expectedConsumedVelocity = velocityThreshold,
- )
- assertIdle(SceneB)
- }
-
- @Test
fun scrollFromIdleWithNoTargetScene_shouldUseOverscrollSpecIfAvailable() = runGestureTest {
layoutState.transitions = transitions {
overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) }
@@ -1531,25 +1327,6 @@
}
@Test
- fun interceptingTransitionKeepsDistance() = runGestureTest {
- var swipeDistance = 75f
- layoutState.transitions = transitions {
- from(SceneA, to = SceneB) { distance = UserActionDistance { _, _, _ -> swipeDistance } }
- }
-
- // Start transition.
- val controller = onDragStarted(overSlop = -50f)
- assertTransition(fromScene = SceneA, toScene = SceneB, progress = 50f / 75f)
-
- // Intercept the transition and change the swipe distance. The original distance and
- // progress should be the same.
- swipeDistance = 50f
- controller.onDragStoppedAnimateLater(0f)
- onDragStartedImmediately()
- assertTransition(fromScene = SceneA, toScene = SceneB, progress = 50f / 75f)
- }
-
- @Test
fun requireFullDistanceSwipe() = runGestureTest {
mutableUserActionsA +=
Swipe.Up to UserActionResult(SceneB, requiresFullDistanceSwipe = true)
@@ -1579,19 +1356,6 @@
}
@Test
- fun interceptingTransitionReplacesCurrentTransition() = runGestureTest {
- val controller = onDragStarted(overSlop = up(fractionOfScreen = 0.5f))
- val transition = assertThat(layoutState.transitionState).isSceneTransition()
- controller.onDragStoppedAnimateLater(velocity = 0f)
-
- // Intercept the transition.
- onDragStartedImmediately()
- val newTransition = assertThat(layoutState.transitionState).isSceneTransition()
- assertThat(newTransition).isNotSameInstanceAs(transition)
- assertThat(newTransition.replacedTransition).isSameInstanceAs(transition)
- }
-
- @Test
fun showOverlay() = runGestureTest {
mutableUserActionsA = mapOf(Swipe.Down to UserActionResult.ShowOverlay(OverlayA))
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index cb3e433..4153350 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -101,7 +101,6 @@
.thenIf(enabled) {
Modifier.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
started = true
SimpleDragController(
@@ -169,8 +168,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- // We want to start a drag gesture immediately
- startDragImmediately = { true },
onDragStarted = { _, _ ->
started = true
SimpleDragController(
@@ -242,7 +239,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
started = true
SimpleDragController(
@@ -361,7 +357,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
started = true
SimpleDragController(
@@ -466,7 +461,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
verticalStarted = true
SimpleDragController(
@@ -478,7 +472,6 @@
)
.multiPointerDraggable(
orientation = Orientation.Horizontal,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
horizontalStarted = true
SimpleDragController(
@@ -570,7 +563,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
swipeDetector =
object : SwipeDetector {
override fun detectSwipe(change: PointerInputChange): Boolean {
@@ -636,7 +628,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
swipeDetector =
object : SwipeDetector {
override fun detectSwipe(change: PointerInputChange): Boolean {
@@ -738,7 +729,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
SimpleDragController(
onDrag = { consumedOnDrag = it },
@@ -809,7 +799,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
SimpleDragController(
onDrag = { /* do nothing */ },