diff options
16 files changed, 155 insertions, 7 deletions
diff --git a/packages/SystemUI/compose/scene/Android.bp b/packages/SystemUI/compose/scene/Android.bp index 682c49cfd19c..090e9ccedda0 100644 --- a/packages/SystemUI/compose/scene/Android.bp +++ b/packages/SystemUI/compose/scene/Android.bp @@ -42,6 +42,7 @@ android_library { "androidx.compose.material3_material3", "PlatformComposeCore", + "mechanics", ], kotlincflags: ["-Xjvm-default=all"], diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateOverlay.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateOverlay.kt index 28116cb435e4..7d41a266adf2 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateOverlay.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateOverlay.kt @@ -17,6 +17,7 @@ package com.android.compose.animation.scene import com.android.compose.animation.scene.content.state.TransitionState +import com.android.mechanics.GestureContext import kotlinx.coroutines.CoroutineScope /** Trigger a one-off transition to show or hide an overlay. */ @@ -118,6 +119,7 @@ private class OneOffShowOrHideOverlayTransition( override val isInitiatedByUserInput: Boolean = false override val isUserInputOngoing: Boolean = false + override val gestureContext: GestureContext? = null override suspend fun run() { oneOffAnimation.run() @@ -144,6 +146,7 @@ private class OneOffOverlayReplacingTransition( override val isInitiatedByUserInput: Boolean = false override val isUserInputOngoing: Boolean = false + override val gestureContext: GestureContext? = null override suspend fun run() { oneOffAnimation.run() diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt index 86be4a44f3cb..dad4e2491be0 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt @@ -17,6 +17,7 @@ package com.android.compose.animation.scene import com.android.compose.animation.scene.content.state.TransitionState +import com.android.mechanics.GestureContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -193,6 +194,7 @@ private class OneOffSceneTransition( get() = oneOffAnimation.progressVelocity override val isUserInputOngoing: Boolean = false + override val gestureContext: GestureContext? = null override suspend fun run() { oneOffAnimation.run() 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 633328a836e3..916d85a80e77 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 @@ -31,6 +31,8 @@ import com.android.compose.animation.scene.content.state.TransitionState.Compani import com.android.compose.animation.scene.effect.GestureEffect import com.android.compose.gesture.NestedDraggable import com.android.compose.ui.util.SpaceVectorConverter +import com.android.mechanics.DistanceGestureContext +import com.android.mechanics.spec.InputDirection import kotlin.math.absoluteValue import kotlinx.coroutines.launch @@ -114,7 +116,14 @@ internal class DraggableHandler( else -> error("Unknown result $result ($upOrLeftResult $downOrRightResult)") } - return createSwipeAnimation(layoutImpl, result, isUpOrLeft, orientation) + val gestureContext = + DistanceGestureContext( + initialDragOffset = 0f, + initialDirection = if (isUpOrLeft) InputDirection.Min else InputDirection.Max, + directionChangeSlop = layoutImpl.directionChangeSlop, + ) + + return createSwipeAnimation(layoutImpl, result, isUpOrLeft, orientation, gestureContext) } private fun resolveSwipeSource(startedPosition: Offset): SwipeSource.Resolved? { @@ -316,6 +325,7 @@ private class DragControllerImpl( // when the distance is defined. delta } + distance > 0f -> desiredOffset.fastCoerceIn(0f, distance) else -> desiredOffset.fastCoerceIn(distance, 0f) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt index 8a6a0d6dbb99..621166e1823a 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt @@ -26,6 +26,8 @@ import com.android.compose.animation.scene.UserActionResult.HideOverlay import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay import com.android.compose.animation.scene.UserActionResult.ShowOverlay import com.android.compose.animation.scene.transition.animateProgress +import com.android.mechanics.ProvidedGestureContext +import com.android.mechanics.spec.InputDirection import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map @@ -55,6 +57,8 @@ internal fun PredictiveBackHandler( // compute the distance. In our case the distance is always 1f. orientation = Orientation.Horizontal, distance = 1f, + gestureContext = + ProvidedGestureContext(dragOffset = 0f, direction = InputDirection.Max), ) animateProgress( 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 4e389471d1d0..ef11932867a0 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 @@ -30,6 +30,7 @@ import androidx.compose.ui.input.pointer.PointerType import androidx.compose.ui.layout.LookaheadScope import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset @@ -716,6 +717,7 @@ internal fun SceneTransitionLayoutForTesting( builder: SceneTransitionLayoutScope.() -> Unit, ) { val density = LocalDensity.current + val directionChangeSlop = LocalViewConfiguration.current.touchSlop val layoutDirection = LocalLayoutDirection.current val animationScope = rememberCoroutineScope() val layoutImpl = remember { @@ -731,6 +733,7 @@ internal fun SceneTransitionLayoutForTesting( elements = sharedElementMap, ancestors = ancestors, lookaheadScope = lookaheadScope, + directionChangeSlop = directionChangeSlop, ) .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 b4c449d0566c..38ad0a80fd00 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 @@ -88,6 +88,14 @@ internal class SceneTransitionLayoutImpl( internal val animationScope: CoroutineScope, /** + * Number of pixels a gesture has to travel in the opposite direction to for its intrinsic + * direction to change. + * + * Used to determine the direction of [Transition.gestureContext]. + */ + internal val directionChangeSlop: Float, + + /** * The map of [Element]s. * * Important: [Element]s from this map should never be accessed during composition because the @@ -368,6 +376,7 @@ internal class SceneTransitionLayoutImpl( error("Transition to the same scene is not supported. ${details()}") } } + is UserActionResult.ReplaceByOverlay -> { check(key is OverlayKey) { "ReplaceByOverlay() can only be used for overlays, not scenes. ${details()}" @@ -377,6 +386,7 @@ internal class SceneTransitionLayoutImpl( "Transition to the same overlay is not supported. ${details()}" } } + is UserActionResult.ShowOverlay, is UserActionResult.HideOverlay -> { /* Always valid. */ @@ -443,8 +453,10 @@ internal class SceneTransitionLayoutImpl( maybeAdd(transition.toScene) maybeAdd(transition.fromScene) } + is TransitionState.Transition.ShowOrHideOverlay -> maybeAdd(transition.fromOrToScene) + is TransitionState.Transition.ReplaceOverlay -> {} } } @@ -510,6 +522,7 @@ internal class SceneTransitionLayoutImpl( is TransitionState.Transition.ChangeScene -> {} is TransitionState.Transition.ShowOrHideOverlay -> maybeAdd(transition.overlay) + is TransitionState.Transition.ReplaceOverlay -> { maybeAdd(transition.fromOverlay) maybeAdd(transition.toOverlay) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt index b1d6d1efc264..4137f5f5725b 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt @@ -21,12 +21,13 @@ import androidx.compose.animation.core.AnimationSpec import androidx.compose.animation.core.AnimationVector1D import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.util.fastCoerceIn import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.content.state.TransitionState.Companion.DistanceUnspecified +import com.android.mechanics.GestureContext +import com.android.mechanics.MutableDragOffsetGestureContext import kotlin.math.absoluteValue import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.launch @@ -37,6 +38,7 @@ internal fun createSwipeAnimation( isUpOrLeft: Boolean, orientation: Orientation, distance: Float, + gestureContext: MutableDragOffsetGestureContext, ): SwipeAnimation<*> { return createSwipeAnimation( layoutState, @@ -47,6 +49,7 @@ internal fun createSwipeAnimation( contentForUserActions = { error("Computing contentForUserActions requires a SceneTransitionLayoutImpl") }, + gestureContext = gestureContext, ) } @@ -55,6 +58,7 @@ internal fun createSwipeAnimation( result: UserActionResult, isUpOrLeft: Boolean, orientation: Orientation, + gestureContext: MutableDragOffsetGestureContext, distance: Float = DistanceUnspecified, ): SwipeAnimation<*> { var lastDistance = distance @@ -98,6 +102,7 @@ internal fun createSwipeAnimation( orientation, distance = ::distance, contentForUserActions = { layoutImpl.contentForUserActions().key }, + gestureContext = gestureContext, ) } @@ -108,6 +113,7 @@ private fun createSwipeAnimation( orientation: Orientation, distance: (SwipeAnimation<*>) -> Float, contentForUserActions: () -> ContentKey, + gestureContext: MutableDragOffsetGestureContext, ): SwipeAnimation<*> { fun <T : ContentKey> swipeAnimation(fromContent: T, toContent: T): SwipeAnimation<T> { return SwipeAnimation( @@ -118,6 +124,7 @@ private fun createSwipeAnimation( isUpOrLeft = isUpOrLeft, requiresFullDistanceSwipe = result.requiresFullDistanceSwipe, distance = distance, + gestureContext = gestureContext, ) } @@ -132,6 +139,7 @@ private fun createSwipeAnimation( ) .swipeAnimation } + is UserActionResult.ShowOverlay -> { val fromScene = layoutState.currentScene val overlay = result.overlay @@ -144,6 +152,7 @@ private fun createSwipeAnimation( ) .swipeAnimation } + is UserActionResult.HideOverlay -> { val toScene = layoutState.currentScene val overlay = result.overlay @@ -156,11 +165,13 @@ private fun createSwipeAnimation( ) .swipeAnimation } + is UserActionResult.ReplaceByOverlay -> { val fromOverlay = when (val contentForUserActions = contentForUserActions()) { is SceneKey -> error("ReplaceByOverlay can only be called when an overlay is shown") + is OverlayKey -> contentForUserActions } @@ -186,8 +197,8 @@ internal class SwipeAnimation<T : ContentKey>( val requiresFullDistanceSwipe: Boolean, private val distance: (SwipeAnimation<T>) -> Float, currentContent: T = fromContent, - dragOffset: Float = 0f, -) { + private val gestureContext: MutableDragOffsetGestureContext, +) : MutableDragOffsetGestureContext by gestureContext { /** The [TransitionState.Transition] whose implementation delegates to this [SwipeAnimation]. */ lateinit var contentTransition: TransitionState.Transition @@ -254,9 +265,6 @@ internal class SwipeAnimation<T : ContentKey>( val isInPreviewStage: Boolean get() = contentTransition.previewTransformationSpec != null && currentContent == fromContent - /** The current offset caused by the drag gesture. */ - var dragOffset by mutableFloatStateOf(dragOffset) - /** The offset animation that animates the offset once the user lifts their finger. */ private var offsetAnimation: Animatable<Float, AnimationVector1D>? by mutableStateOf(null) private val offsetAnimationRunnable = CompletableDeferred<suspend () -> Unit>() @@ -387,6 +395,7 @@ internal class SwipeAnimation<T : ContentKey>( return when (val transition = contentTransition) { is TransitionState.Transition.ChangeScene -> layoutState.canChangeScene(targetContent as SceneKey) + is TransitionState.Transition.ShowOrHideOverlay -> { if (targetContent == transition.overlay) { layoutState.canShowOverlay(transition.overlay) @@ -394,6 +403,7 @@ internal class SwipeAnimation<T : ContentKey>( layoutState.canHideOverlay(transition.overlay) } } + is TransitionState.Transition.ReplaceOverlay -> { val to = targetContent as OverlayKey val from = @@ -464,6 +474,8 @@ private class ChangeSceneSwipeTransition( override val isUserInputOngoing: Boolean get() = swipeAnimation.isUserInputOngoing + override val gestureContext: GestureContext = swipeAnimation + override suspend fun run() { swipeAnimation.run() } @@ -515,6 +527,8 @@ private class ShowOrHideOverlaySwipeTransition( override val isUserInputOngoing: Boolean get() = swipeAnimation.isUserInputOngoing + override val gestureContext: GestureContext = swipeAnimation + override suspend fun run() { swipeAnimation.run() } @@ -562,6 +576,8 @@ private class ReplaceOverlaySwipeTransition( override val isUserInputOngoing: Boolean get() = swipeAnimation.isUserInputOngoing + override val gestureContext: GestureContext = swipeAnimation + override suspend fun run() { swipeAnimation.run() } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt index f772f1a0b4a6..e9542c830b4d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt @@ -33,6 +33,7 @@ import com.android.compose.animation.scene.TransformationSpec import com.android.compose.animation.scene.TransformationSpecImpl import com.android.compose.animation.scene.TransitionKey import com.android.internal.jank.Cuj.CujType +import com.android.mechanics.GestureContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch @@ -238,6 +239,9 @@ sealed interface TransitionState { /** Whether user input is currently driving the transition. */ abstract val isUserInputOngoing: Boolean + /** Additional gesture context whenever the transition is driven by a user gesture. */ + abstract val gestureContext: GestureContext? + /** The CUJ covered by this transition. */ @CujType val cuj: Int? diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt index c6912d5e4ad2..819cec712808 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt @@ -29,6 +29,8 @@ import com.android.compose.animation.scene.SwipeAnimation import com.android.compose.animation.scene.TransitionKey import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.createSwipeAnimation +import com.android.mechanics.ProvidedGestureContext +import com.android.mechanics.spec.InputDirection import kotlin.coroutines.cancellation.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -126,6 +128,9 @@ private suspend fun MutableSceneTransitionLayoutState.seek( // overscroll, which is disabled for progress-based transitions. orientation = Orientation.Horizontal, isUpOrLeft = false, + // There is no gesture information available here - animateProgress + // will set the progress as the dragOffset. + gestureContext = ProvidedGestureContext(0f, InputDirection.Max), ) animateProgress( 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 4a0c330be4f8..ef360770bc41 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 @@ -41,7 +41,9 @@ import com.android.compose.animation.scene.subjects.assertThat import com.android.compose.gesture.NestedDraggable import com.android.compose.test.MonotonicClockTestScope import com.android.compose.test.runMonotonicClockTest +import com.android.mechanics.spec.InputDirection import com.google.common.truth.Truth.assertThat +import kotlin.math.nextUp import kotlin.math.sign import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred @@ -106,6 +108,7 @@ class DraggableHandlerTest { } val transitionInterceptionThreshold = 0.05f + val directionChangeSlop = 10f private val layoutImpl = SceneTransitionLayoutImpl( @@ -120,6 +123,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, + directionChangeSlop = directionChangeSlop, ) .apply { setContentsAndLayoutTargetSizeForTest(LAYOUT_SIZE) } @@ -871,4 +875,65 @@ class DraggableHandlerTest { assertThat(layoutState.transitionState).hasCurrentScene(SceneA) assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayB) } + + @Test + fun gestureContext_dragOffset_matchesOverSlopAtBeginning() = runGestureTest { + val overSlop = down(fractionOfScreen = 0.1f) + onDragStarted(overSlop = overSlop) + + val gestureContext = assertThat(transitionState).hasGestureContext() + assertThat(gestureContext.dragOffset).isEqualTo(overSlop) + } + + @Test + fun gestureContext_dragOffset_getsUpdatedOnEachDragEvent() = runGestureTest { + val dragController = onDragStarted(overSlop = down(fractionOfScreen = 0.1f)) + + val gestureContext = assertThat(transitionState).hasGestureContext() + val initialDragOffset = gestureContext.dragOffset + + dragController.onDragDelta(pixels = 3.5f) + assertThat(gestureContext.dragOffset).isEqualTo(initialDragOffset + 3.5f) + + dragController.onDragDelta(pixels = -2f) + assertThat(gestureContext.dragOffset).isEqualTo(initialDragOffset + 3.5f - 2f) + } + + @Test + fun gestureContext_direction_swipeDown_startsWithMaxDirection() = runGestureTest { + onDragStarted(overSlop = down(fractionOfScreen = 0.1f)) + + val gestureContext = assertThat(transitionState).hasGestureContext() + assertThat(gestureContext.direction).isEqualTo(InputDirection.Max) + } + + @Test + fun gestureContext_direction_swipeUp_startsWithMinDirection() = runGestureTest { + onDragStarted(overSlop = up(fractionOfScreen = 0.1f)) + + val gestureContext = assertThat(transitionState).hasGestureContext() + assertThat(gestureContext.direction).isEqualTo(InputDirection.Min) + } + + @Test + fun gestureContext_direction_withinDirectionSlop_staysSame() = runGestureTest { + val dragController = onDragStarted(overSlop = up(fractionOfScreen = .2f)) + + val gestureContext = assertThat(transitionState).hasGestureContext() + assertThat(gestureContext.direction).isEqualTo(InputDirection.Min) + + dragController.onDragDelta(pixels = directionChangeSlop) + assertThat(gestureContext.direction).isEqualTo(InputDirection.Min) + } + + @Test + fun gestureContext_direction_overDirectionSlop_isChanged() = runGestureTest { + val dragController = onDragStarted(overSlop = up(fractionOfScreen = .2f)) + + val gestureContext = assertThat(transitionState).hasGestureContext() + assertThat(gestureContext.direction).isEqualTo(InputDirection.Min) + + dragController.onDragDelta(pixels = directionChangeSlop.nextUp()) + assertThat(gestureContext.direction).isEqualTo(InputDirection.Max) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt index 6db98a874787..8db7dbca2474 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt @@ -19,6 +19,7 @@ package com.android.compose.animation.scene.subjects import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.content.state.TransitionState +import com.android.mechanics.GestureContext import com.google.common.truth.Fact.simpleFact import com.google.common.truth.FailureMetadata import com.google.common.truth.Subject @@ -98,6 +99,17 @@ private constructor(metadata: FailureMetadata, private val actual: TransitionSta return actual as TransitionState.Transition.ReplaceOverlay } + fun hasGestureContext(): GestureContext { + if (actual !is TransitionState.Transition) { + failWithActual(simpleFact("expected to be TransitionState.Transition")) + } + + val gestureContext = ((actual as TransitionState.Transition).gestureContext) + check("transition.gestureContext").that(gestureContext).isNotNull() + + return checkNotNull(gestureContext) + } + companion object { fun transitionStates() = Factory { metadata, actual: TransitionState -> TransitionStateSubject(metadata, actual) diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt index b9d01c2eaa3b..c22c19867dbb 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt @@ -20,6 +20,7 @@ import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.content.state.TransitionState.Transition +import com.android.mechanics.GestureContext import kotlinx.coroutines.CompletableDeferred /** A [Transition.ShowOrHideOverlay] for tests that will be finished once [finish] is called. */ @@ -84,6 +85,7 @@ fun transition( override val isInitiatedByUserInput: Boolean = isInitiatedByUserInput override val isUserInputOngoing: Boolean = isUserInputOngoing + override val gestureContext: GestureContext? = null override fun freezeAndAnimateToCurrentState() { if (onFreezeAndAnimate != null) { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt index 983c429aa58e..139dcd5d5114 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt @@ -19,6 +19,7 @@ package com.android.compose.test import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.content.state.TransitionState.Transition +import com.android.mechanics.GestureContext import kotlinx.coroutines.CompletableDeferred /** A [Transition.ShowOrHideOverlay] for tests that will be finished once [finish] is called. */ @@ -81,6 +82,7 @@ fun transition( override val isInitiatedByUserInput: Boolean = isInitiatedByUserInput override val isUserInputOngoing: Boolean = isUserInputOngoing + override val gestureContext: GestureContext? = null override fun freezeAndAnimateToCurrentState() { if (onFreezeAndAnimate != null) { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt index d11951ee4b24..18cd57bb985f 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt @@ -19,6 +19,7 @@ package com.android.compose.test import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.content.state.TransitionState.Transition +import com.android.mechanics.GestureContext import kotlinx.coroutines.CompletableDeferred /** A [Transition.ChangeScene] for tests that will be finished once [finish] is called. */ @@ -54,6 +55,7 @@ fun transition( isUserInputOngoing: Boolean = false, onFreezeAndAnimate: ((TestSceneTransition) -> Unit)? = null, replacedTransition: Transition? = null, + gestureContext: GestureContext? = null, ): TestSceneTransition { return object : TestSceneTransition(from, to, replacedTransition) { override val currentScene: SceneKey @@ -76,6 +78,7 @@ fun transition( override val isInitiatedByUserInput: Boolean = isInitiatedByUserInput override val isUserInputOngoing: Boolean = isUserInputOngoing + override val gestureContext: GestureContext? = gestureContext override fun freezeAndAnimateToCurrentState() { if (onFreezeAndAnimate != null) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt index 56fece8ecf5c..65fba28c5465 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt @@ -100,6 +100,7 @@ import com.android.compose.modifiers.height import com.android.compose.modifiers.padding import com.android.compose.modifiers.thenIf import com.android.compose.theme.PlatformTheme +import com.android.mechanics.GestureContext import com.android.systemui.Dumpable import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer import com.android.systemui.compose.modifiers.sysuiResTag @@ -935,6 +936,8 @@ private class ExpansionTransition(currentProgress: Float) : override val isUserInputOngoing: Boolean get() = true + override val gestureContext: GestureContext? = null + private val finishCompletable = CompletableDeferred<Unit>() override suspend fun run() { |