diff options
39 files changed, 451 insertions, 462 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt index bcdb259f161f..eae46e90557a 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt @@ -111,7 +111,7 @@ constructor( 1f } - val dir = if (transition.toScene == splitShadeLargeClockScene) -1f else 1f + val dir = if (transition.toContent == splitShadeLargeClockScene) -1f else 1f val distance = dir * getClockCenteringDistance() val largeClock = checkNotNull(currentClock).largeClock largeClock.animations.onPositionUpdated( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt index 70c0db1582c4..5dccb681b041 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt @@ -21,7 +21,7 @@ import com.android.compose.animation.scene.ElementContentPicker import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.SceneTransitionLayoutState import com.android.compose.animation.scene.StaticElementContentPicker -import com.android.compose.animation.scene.content.state.ContentState +import com.android.compose.animation.scene.content.state.TransitionState import com.android.systemui.scene.shared.model.Scenes /** [ElementContentPicker] implementation for the media carousel object. */ @@ -38,7 +38,7 @@ object MediaContentPicker : StaticElementContentPicker { override fun contentDuringTransition( element: ElementKey, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, fromContentZIndex: Float, toContentZIndex: Float ): ContentKey { @@ -64,7 +64,7 @@ object MediaContentPicker : StaticElementContentPicker { } /** Returns true when the media should be laid on top of the rest for the given [transition]. */ - fun shouldElevateMedia(transition: ContentState.Transition<*>): Boolean { + fun shouldElevateMedia(transition: TransitionState.Transition): Boolean { return transition.isTransitioningBetween(Scenes.Lockscreen, Scenes.Shade) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt index 055534604734..fc4a8a5ee67c 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt @@ -98,7 +98,7 @@ private fun SceneScope.stateForQuickSettingsContent( else -> QSSceneAdapter.State.CLOSED } } - is TransitionState.Transition -> + is TransitionState.Transition.ChangeCurrentScene -> with(transitionState) { when { isSplitShade -> UnsquishingQS(squishiness) @@ -108,16 +108,16 @@ private fun SceneScope.stateForQuickSettingsContent( fromScene == Scenes.QuickSettings && toScene == Scenes.Shade -> { Collapsing { progress } } - fromScene == Scenes.Shade || toScene == Scenes.Shade -> { + fromContent == Scenes.Shade || toContent == Scenes.Shade -> { UnsquishingQQS(squishiness) } - fromScene == Scenes.QuickSettings || toScene == Scenes.QuickSettings -> { + fromContent == Scenes.QuickSettings || toContent == Scenes.QuickSettings -> { QSSceneAdapter.State.QS } else -> error( - "Bad transition for QuickSettings: fromScene=$fromScene," + - " toScene=$toScene" + "Bad transition for QuickSettings: fromContent=$fromContent," + + " toScene=$toContent" ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt index e0647242b7ad..d16ba0d6affb 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt @@ -253,7 +253,7 @@ private fun SceneScope.QuickSettingsScene( val isScrollable = when (val state = layoutState.transitionState) { is TransitionState.Idle -> true - is TransitionState.Transition -> state.fromScene == Scenes.QuickSettings + is TransitionState.Transition -> state.fromContent == Scenes.QuickSettings } LaunchedEffect(isCustomizing, scrollState) { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt index 5eabd2275285..2bc8c87978a8 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt @@ -19,14 +19,14 @@ package com.android.compose.animation.scene import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationVector1D import androidx.compose.animation.core.SpringSpec -import com.android.compose.animation.scene.content.state.ContentState +import com.android.compose.animation.scene.content.state.TransitionState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Job import kotlinx.coroutines.launch internal fun CoroutineScope.animateContent( - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, oneOffAnimation: OneOffAnimation, targetProgress: Float, startTransition: () -> Unit, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt index ae5a84b859e5..ea708a5637f0 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt @@ -396,8 +396,8 @@ private class AnimatedStateImpl<T, Delta>( return sharedValue[layoutImpl.state.transitionState.currentScene] } - val fromValue = sharedValue[transition.fromScene] - val toValue = sharedValue[transition.toScene] + val fromValue = sharedValue[transition.fromContent] + val toValue = sharedValue[transition.toContent] return if (fromValue != null && toValue != null) { if (fromValue == toValue) { // Optimization: avoid reading progress if the values are the same, so we don't @@ -411,7 +411,7 @@ private class AnimatedStateImpl<T, Delta>( if (canOverflow) transition.progress else transition.progress.fastCoerceIn(0f, 1f) } - overscrollSpec.scene == transition.toScene -> 1f + overscrollSpec.scene == transition.toContent -> 1f else -> 0f } @@ -426,12 +426,12 @@ private class AnimatedStateImpl<T, Delta>( if (element != null) { layoutImpl.elements[element]?.stateByContent?.let { sceneStates -> layoutImpl.state.currentTransitions.fastLastOrNull { transition -> - transition.fromScene in sceneStates || transition.toScene in sceneStates + transition.fromContent in sceneStates || transition.toContent in sceneStates } } } else { layoutImpl.state.currentTransitions.fastLastOrNull { transition -> - transition.fromScene in targetValues || transition.toScene in targetValues + transition.fromContent in targetValues || transition.toContent in targetValues } } 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 920c234174d0..f2c2a3600366 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 @@ -28,7 +28,7 @@ internal fun CoroutineScope.animateToScene( layoutState: MutableSceneTransitionLayoutStateImpl, target: SceneKey, transitionKey: TransitionKey?, -): TransitionState.Transition? { +): TransitionState.Transition.ChangeCurrentScene? { val transitionState = layoutState.transitionState if (transitionState.currentScene == target) { // This can happen in 3 different situations, for which there isn't anything else to do: @@ -52,7 +52,7 @@ internal fun CoroutineScope.animateToScene( replacedTransition = null, ) } - is TransitionState.Transition -> { + is TransitionState.Transition.ChangeCurrentScene -> { val isInitiatedByUserInput = transitionState.isInitiatedByUserInput // A transition is currently running: first check whether `transition.toScene` or @@ -136,7 +136,7 @@ private fun CoroutineScope.animateToScene( reversed: Boolean = false, fromScene: SceneKey = layoutState.transitionState.currentScene, chain: Boolean = true, -): TransitionState.Transition { +): TransitionState.Transition.ChangeCurrentScene { val oneOffAnimation = OneOffAnimation() val targetProgress = if (reversed) 0f else 1f val transition = @@ -181,7 +181,7 @@ private class OneOffSceneTransition( override val isInitiatedByUserInput: Boolean, replacedTransition: TransitionState.Transition?, private val oneOffAnimation: OneOffAnimation, -) : TransitionState.Transition(fromScene, toScene, replacedTransition) { +) : TransitionState.Transition.ChangeCurrentScene(fromScene, toScene, replacedTransition) { override val progress: Float get() = oneOffAnimation.progress 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 83db7248f9dd..778745869bad 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,9 +31,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.round import androidx.compose.ui.util.fastCoerceIn import com.android.compose.animation.scene.content.Scene -import com.android.compose.animation.scene.content.state.ContentState -import com.android.compose.animation.scene.content.state.ContentState.HasOverscrollProperties.Companion.DistanceUnspecified import com.android.compose.animation.scene.content.state.TransitionState +import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified import com.android.compose.nestedscroll.PriorityNestedScrollConnection import kotlin.math.absoluteValue import kotlinx.coroutines.CoroutineScope @@ -582,8 +581,8 @@ private class SwipeTransition( replacedTransition: SwipeTransition?, var lastDistance: Float = DistanceUnspecified, ) : - TransitionState.Transition(_fromScene.key, _toScene.key, replacedTransition), - ContentState.HasOverscrollProperties { + TransitionState.Transition.ChangeCurrentScene(_fromScene.key, _toScene.key, replacedTransition), + TransitionState.HasOverscrollProperties { var _currentScene by mutableStateOf(_fromScene) override val currentScene: SceneKey get() = _currentScene.key diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index e6198148d265..7dac2e4c4ada 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -49,7 +49,6 @@ import androidx.compose.ui.util.fastCoerceIn import androidx.compose.ui.util.fastLastOrNull import androidx.compose.ui.util.lerp import com.android.compose.animation.scene.content.Content -import com.android.compose.animation.scene.content.state.ContentState import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.transformation.PropertyTransformation import com.android.compose.animation.scene.transformation.SharedElementTransformation @@ -69,7 +68,7 @@ internal class Element(val key: ElementKey) { * The last transition that was used when computing the state (size, position and alpha) of this * element in any content, or `null` if it was last laid out when idle. */ - var lastTransition: ContentState.Transition<*>? = null + var lastTransition: TransitionState.Transition? = null /** Whether this element was ever drawn in a content. */ var wasDrawnInAnyContent = false @@ -312,7 +311,7 @@ internal class ElementNode( } private fun Placeable.PlacementScope.place( - transition: ContentState.Transition<*>?, + transition: TransitionState.Transition?, placeable: Placeable, ) { with(layoutImpl.lookaheadScope) { @@ -477,11 +476,11 @@ private fun elementTransition( layoutImpl: SceneTransitionLayoutImpl, element: Element, transitions: List<TransitionState.Transition>, -): ContentState.Transition<*>? { +): TransitionState.Transition? { val transition = transitions.fastLastOrNull { transition -> - transition.fromScene in element.stateByContent || - transition.toScene in element.stateByContent + transition.fromContent in element.stateByContent || + transition.toContent in element.stateByContent } val previousTransition = element.lastTransition @@ -504,8 +503,8 @@ private fun elementTransition( private fun prepareInterruption( layoutImpl: SceneTransitionLayoutImpl, element: Element, - transition: ContentState.Transition<*>, - previousTransition: ContentState.Transition<*>, + transition: TransitionState.Transition, + previousTransition: TransitionState.Transition, ) { if (transition.replacedTransition == previousTransition) { return @@ -552,7 +551,7 @@ private fun prepareInterruption( */ private fun reconcileStates( element: Element, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, ) { val fromContentState = element.stateByContent[transition.fromContent] ?: return val toContentState = element.stateByContent[transition.toContent] ?: return @@ -621,7 +620,7 @@ private fun Element.State.clearValuesBeforeInterruption() { */ private inline fun <T> computeInterruptedValue( layoutImpl: SceneTransitionLayoutImpl, - transition: ContentState.Transition<*>?, + transition: TransitionState.Transition?, value: T, unspecifiedValue: T, zeroValue: T, @@ -668,7 +667,7 @@ private inline fun <T> computeInterruptedValue( private inline fun <T> setPlacementInterruptionDelta( element: Element, stateInContent: Element.State, - transition: ContentState.Transition<*>?, + transition: TransitionState.Transition?, delta: T, setter: (Element.State, T) -> Unit, ) { @@ -694,7 +693,7 @@ private fun shouldPlaceElement( layoutImpl: SceneTransitionLayoutImpl, content: ContentKey, element: Element, - transition: ContentState.Transition<*>?, + transition: TransitionState.Transition?, ): Boolean { // Always place the element if we are idle. if (transition == null) { @@ -732,7 +731,7 @@ internal fun shouldPlaceOrComposeSharedElement( layoutImpl: SceneTransitionLayoutImpl, content: ContentKey, element: ElementKey, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, ): Boolean { // If we are overscrolling, only place/compose the element in the overscrolling scene. val overscrollScene = transition.currentOverscrollSpec?.scene @@ -743,7 +742,7 @@ internal fun shouldPlaceOrComposeSharedElement( val scenePicker = element.contentPicker val pickedScene = when (transition) { - is TransitionState.Transition -> { + is TransitionState.Transition.ChangeCurrentScene -> { scenePicker.contentDuringTransition( element = element, transition = transition, @@ -758,14 +757,14 @@ internal fun shouldPlaceOrComposeSharedElement( private fun isSharedElementEnabled( element: ElementKey, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, ): Boolean { return sharedElementTransformation(element, transition)?.enabled ?: true } internal fun sharedElementTransformation( element: ElementKey, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, ): SharedElementTransformation? { val transformationSpec = transition.transformationSpec val sharedInFromContent = @@ -793,7 +792,7 @@ internal fun sharedElementTransformation( private fun isElementOpaque( content: Content, element: Element, - transition: ContentState.Transition<*>?, + transition: TransitionState.Transition?, ): Boolean { if (transition == null) { return true @@ -827,7 +826,7 @@ private fun isElementOpaque( private fun elementAlpha( layoutImpl: SceneTransitionLayoutImpl, element: Element, - transition: ContentState.Transition<*>?, + transition: TransitionState.Transition?, stateInContent: Element.State, ): Float { val alpha = @@ -858,7 +857,7 @@ private fun elementAlpha( private fun interruptedAlpha( layoutImpl: SceneTransitionLayoutImpl, element: Element, - transition: ContentState.Transition<*>?, + transition: TransitionState.Transition?, stateInContent: Element.State, alpha: Float, ): Float { @@ -888,7 +887,7 @@ private fun interruptedAlpha( private fun measure( layoutImpl: SceneTransitionLayoutImpl, element: Element, - transition: ContentState.Transition<*>?, + transition: TransitionState.Transition?, stateInContent: Element.State, measurable: Measurable, constraints: Constraints, @@ -952,7 +951,7 @@ private fun Placeable.size(): IntSize = IntSize(width, height) private fun ContentDrawScope.getDrawScale( layoutImpl: SceneTransitionLayoutImpl, element: Element, - transition: ContentState.Transition<*>?, + transition: TransitionState.Transition?, stateInContent: Element.State, ): Scale { val scale = @@ -1048,7 +1047,7 @@ private inline fun <T> computeValue( layoutImpl: SceneTransitionLayoutImpl, currentContentState: Element.State, element: Element, - transition: ContentState.Transition<*>?, + transition: TransitionState.Transition?, contentValue: (Element.State) -> T, transformation: (ElementTransformations) -> PropertyTransformation<T>?, currentValue: () -> T, @@ -1076,7 +1075,7 @@ private inline fun <T> computeValue( } val currentContent = currentContentState.content - if (transition is ContentState.HasOverscrollProperties) { + if (transition is TransitionState.HasOverscrollProperties) { val overscroll = transition.currentOverscrollSpec if (overscroll?.scene == currentContent) { val elementSpec = diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt index bf70ca9b6744..6181cfbb10eb 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt @@ -37,7 +37,7 @@ interface InterruptionHandler { * @see InterruptionResult */ fun onInterruption( - interrupted: TransitionState.Transition, + interrupted: TransitionState.Transition.ChangeCurrentScene, newTargetScene: SceneKey, ): InterruptionResult? } @@ -76,7 +76,7 @@ class InterruptionResult( */ object DefaultInterruptionHandler : InterruptionHandler { override fun onInterruption( - interrupted: TransitionState.Transition, + interrupted: TransitionState.Transition.ChangeCurrentScene, newTargetScene: SceneKey, ): InterruptionResult { return InterruptionResult( diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt index abecdd771f7a..63d51f9bbcf4 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt @@ -185,7 +185,7 @@ private fun shouldComposeMovableElement( val contents = element.contentPicker.contents val transition = transitions.fastLastOrNull { transition -> - transition.fromScene in contents || transition.toScene in contents + transition.fromContent in contents || transition.toContent in contents } ?: return false // Always compose movable elements in the scene picked by their scene picker. @@ -215,7 +215,8 @@ private fun placeholderContentSize( // This code is only run during transitions (otherwise the content would be composed and the // placeholder would not), so it's ok to cast the state into a Transition directly. - val transition = layoutImpl.state.transitionState as TransitionState.Transition + val transition = + layoutImpl.state.transitionState as TransitionState.Transition.ChangeCurrentScene // If the content was already composed in the other scene, we use that target size assuming it // doesn't change between scenes. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt index 8f1a4141176a..5071a7f744dc 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt @@ -111,7 +111,7 @@ fun SceneTransitionLayoutState.observableTransitionState(): Flow<ObservableTrans return snapshotFlow { when (val state = transitionState) { is TransitionState.Idle -> ObservableTransitionState.Idle(state.currentScene) - is TransitionState.Transition -> { + is TransitionState.Transition.ChangeCurrentScene -> { ObservableTransitionState.Transition( fromScene = state.fromScene, toScene = state.toScene, 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 cc53a28c848a..e7e6b2a257d8 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 @@ -70,7 +70,7 @@ private class PredictiveBackTransition( val coroutineScope: CoroutineScope, fromScene: SceneKey, toScene: SceneKey, -) : TransitionState.Transition(fromScene, toScene) { +) : TransitionState.Transition.ChangeCurrentScene(fromScene, toScene) { override var currentScene by mutableStateOf(fromScene) private set 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 062d5533c539..392ff7ebb446 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 @@ -38,6 +38,7 @@ import androidx.compose.ui.util.fastForEach import androidx.compose.ui.util.fastForEachReversed import com.android.compose.animation.scene.content.Content import com.android.compose.animation.scene.content.Scene +import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.ui.util.lerp import kotlinx.coroutines.CoroutineScope @@ -247,8 +248,12 @@ internal class SceneTransitionLayoutImpl( // Compose the new scene we are going to first. transitions.fastForEachReversed { transition -> - maybeAdd(transition.toScene) - maybeAdd(transition.fromScene) + when (transition) { + is TransitionState.Transition.ChangeCurrentScene -> { + maybeAdd(transition.toScene) + maybeAdd(transition.fromScene) + } + } } } } @@ -284,7 +289,8 @@ private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) : val width: Int val height: Int - val transition = layoutImpl.state.currentTransition + val transition = + layoutImpl.state.currentTransition as? TransitionState.Transition.ChangeCurrentScene if (transition == null) { width = placeable.width height = placeable.height diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index 44f5964feacb..f37ded0547ea 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -25,7 +25,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.util.fastAll import androidx.compose.ui.util.fastFilter import androidx.compose.ui.util.fastForEach -import com.android.compose.animation.scene.content.state.ContentState import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.transition.link.LinkedTransition import com.android.compose.animation.scene.transition.link.StateLink @@ -209,7 +208,7 @@ internal class MutableSceneTransitionLayoutStateImpl( targetScene: SceneKey, coroutineScope: CoroutineScope, transitionKey: TransitionKey?, - ): TransitionState.Transition? { + ): TransitionState.Transition.ChangeCurrentScene? { checkThread() return coroutineScope.animateToScene( @@ -228,13 +227,16 @@ internal class MutableSceneTransitionLayoutStateImpl( * * Important: you *must* call [finishTransition] once the transition is finished. */ - internal fun startTransition(transition: TransitionState.Transition, chain: Boolean = true) { + internal fun startTransition( + transition: TransitionState.Transition.ChangeCurrentScene, + chain: Boolean = true, + ) { checkThread() // Compute the [TransformationSpec] when the transition starts. val fromScene = transition.fromScene val toScene = transition.toScene - val orientation = (transition as? ContentState.HasOverscrollProperties)?.orientation + val orientation = (transition as? TransitionState.HasOverscrollProperties)?.orientation // Update the transition specs. transition.transformationSpec = @@ -312,8 +314,8 @@ internal class MutableSceneTransitionLayoutStateImpl( appendLine(" Transitions (size=${transitionStates.size}):") transitionStates.fastForEach { state -> val transition = state as TransitionState.Transition - val from = transition.fromScene - val to = transition.toScene + val from = transition.fromContent + val to = transition.toContent val indicator = if (finishedTransitions.contains(transition)) "x" else " " appendLine(" [$indicator] $from => $to ($transition)") } @@ -329,27 +331,31 @@ internal class MutableSceneTransitionLayoutStateImpl( } private fun setupTransitionLinks(transition: TransitionState.Transition) { - stateLinks.fastForEach { stateLink -> - val matchingLinks = - stateLink.transitionLinks.fastFilter { it.isMatchingLink(transition) } - if (matchingLinks.isEmpty()) return@fastForEach - if (matchingLinks.size > 1) error("More than one link matched.") - - val targetCurrentScene = stateLink.target.transitionState.currentScene - val matchingLink = matchingLinks[0] - - if (!matchingLink.targetIsInValidState(targetCurrentScene)) return@fastForEach - - val linkedTransition = - LinkedTransition( - originalTransition = transition, - fromScene = targetCurrentScene, - toScene = matchingLink.targetTo, - key = matchingLink.targetTransitionKey, - ) - - stateLink.target.startTransition(linkedTransition) - transition.activeTransitionLinks[stateLink] = linkedTransition + when (transition) { + is TransitionState.Transition.ChangeCurrentScene -> { + stateLinks.fastForEach { stateLink -> + val matchingLinks = + stateLink.transitionLinks.fastFilter { it.isMatchingLink(transition) } + if (matchingLinks.isEmpty()) return@fastForEach + if (matchingLinks.size > 1) error("More than one link matched.") + + val targetCurrentScene = stateLink.target.transitionState.currentScene + val matchingLink = matchingLinks[0] + + if (!matchingLink.targetIsInValidState(targetCurrentScene)) return@fastForEach + + val linkedTransition = + LinkedTransition( + originalTransition = transition, + fromScene = targetCurrentScene, + toScene = matchingLink.targetTo, + key = matchingLink.targetTransitionKey, + ) + + stateLink.target.startTransition(linkedTransition) + transition.activeTransitionLinks[stateLink] = linkedTransition + } + } } } @@ -451,8 +457,8 @@ internal class MutableSceneTransitionLayoutStateImpl( } val shouldSnap = - (isProgressCloseTo(0f) && transition.currentScene == transition.fromScene) || - (isProgressCloseTo(1f) && transition.currentScene == transition.toScene) + (isProgressCloseTo(0f) && transition.currentScene == transition.fromContent) || + (isProgressCloseTo(1f) && transition.currentScene == transition.toContent) return if (shouldSnap) { finishAllTransitions() true diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt index e38c849182d8..5cc194d32424 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt @@ -25,7 +25,7 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.android.compose.animation.scene.content.state.ContentState +import com.android.compose.animation.scene.content.state.TransitionState import kotlin.math.tanh /** Define the [transitions][SceneTransitions] to be used with a [SceneTransitionLayout]. */ @@ -262,7 +262,7 @@ interface ElementContentPicker { */ fun contentDuringTransition( element: ElementKey, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, fromContentZIndex: Float, toContentZIndex: Float, ): ContentKey @@ -278,7 +278,7 @@ interface ElementContentPicker { */ fun pickSingleContentIn( contents: Set<ContentKey>, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, element: ElementKey, ): ContentKey { val fromContent = transition.fromContent @@ -331,7 +331,7 @@ interface StaticElementContentPicker : ElementContentPicker { object HighestZIndexContentPicker : ElementContentPicker { override fun contentDuringTransition( element: ElementKey, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, fromContentZIndex: Float, toContentZIndex: Float ): ContentKey { @@ -352,7 +352,7 @@ object HighestZIndexContentPicker : ElementContentPicker { override fun contentDuringTransition( element: ElementKey, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, fromContentZIndex: Float, toContentZIndex: Float ): ContentKey { @@ -373,7 +373,7 @@ object HighestZIndexContentPicker : ElementContentPicker { object LowestZIndexContentPicker : ElementContentPicker { override fun contentDuringTransition( element: ElementKey, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, fromContentZIndex: Float, toContentZIndex: Float ): ContentKey { @@ -394,7 +394,7 @@ object LowestZIndexContentPicker : ElementContentPicker { override fun contentDuringTransition( element: ElementKey, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, fromContentZIndex: Float, toContentZIndex: Float ): ContentKey { @@ -428,7 +428,7 @@ class MovableElementContentPicker( ) : StaticElementContentPicker { override fun contentDuringTransition( element: ElementKey, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, fromContentZIndex: Float, toContentZIndex: Float, ): ContentKey { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt deleted file mode 100644 index 0bd676bb9858..000000000000 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.compose.animation.scene.content.state - -import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.AnimationVector1D -import androidx.compose.animation.core.spring -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.runtime.Stable -import com.android.compose.animation.scene.ContentKey -import com.android.compose.animation.scene.OverscrollScope -import com.android.compose.animation.scene.OverscrollSpecImpl -import com.android.compose.animation.scene.ProgressVisibilityThreshold -import com.android.compose.animation.scene.SceneTransitionLayoutImpl -import com.android.compose.animation.scene.TransformationSpec -import com.android.compose.animation.scene.TransformationSpecImpl -import com.android.compose.animation.scene.TransitionKey -import com.android.compose.animation.scene.transition.link.LinkedTransition -import com.android.compose.animation.scene.transition.link.StateLink -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch - -/** The state associated to one or more contents. */ -@Stable -sealed interface ContentState<out T : ContentKey> { - /** The [content] is idle, it does not animate. */ - sealed class Idle<T : ContentKey>(val content: T) : ContentState<T> - - /** The content is transitioning with another content. */ - sealed class Transition<out T : ContentKey>( - val fromContent: T, - val toContent: T, - internal val replacedTransition: Transition<T>?, - ) : ContentState<T> { - /** - * The key of this transition. This should usually be null, but it can be specified to use a - * specific set of transformations associated to this transition. - */ - open val key: TransitionKey? = null - - /** - * The progress of the transition. This is usually in the `[0; 1]` range, but it can also be - * less than `0` or greater than `1` when using transitions with a spring AnimationSpec or - * when flinging quickly during a swipe gesture. - */ - abstract val progress: Float - - /** The current velocity of [progress], in progress units. */ - abstract val progressVelocity: Float - - /** Whether the transition was triggered by user input rather than being programmatic. */ - abstract val isInitiatedByUserInput: Boolean - - /** Whether user input is currently driving the transition. */ - abstract val isUserInputOngoing: Boolean - - /** - * The progress of the preview transition. This is usually in the `[0; 1]` range, but it can - * also be less than `0` or greater than `1` when using transitions with a spring - * AnimationSpec or when flinging quickly during a swipe gesture. - */ - internal open val previewProgress: Float = 0f - - /** The current velocity of [previewProgress], in progress units. */ - internal open val previewProgressVelocity: Float = 0f - - /** Whether the transition is currently in the preview stage */ - internal open val isInPreviewStage: Boolean = false - - /** - * The current [TransformationSpecImpl] and [OverscrollSpecImpl] associated to this - * transition. - * - * Important: These will be set exactly once, when this transition is - * [started][MutableSceneTransitionLayoutStateImpl.startTransition]. - */ - internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty - internal var previewTransformationSpec: TransformationSpecImpl? = null - private var fromOverscrollSpec: OverscrollSpecImpl? = null - private var toOverscrollSpec: OverscrollSpecImpl? = null - - /** The current [OverscrollSpecImpl], if this transition is currently overscrolling. */ - internal val currentOverscrollSpec: OverscrollSpecImpl? - get() { - if (this !is HasOverscrollProperties) return null - val progress = progress - val bouncingContent = bouncingContent - return when { - progress < 0f || bouncingContent == fromContent -> fromOverscrollSpec - progress > 1f || bouncingContent == toContent -> toOverscrollSpec - else -> null - } - } - - /** - * An animatable that animates from 1f to 0f. This will be used to nicely animate the sudden - * jump of values when this transitions interrupts another one. - */ - private var interruptionDecay: Animatable<Float, AnimationVector1D>? = null - - /** The map of active links that connects this transition to other transitions. */ - internal val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>() - - init { - check(fromContent != toContent) - check( - replacedTransition == null || - (replacedTransition.fromContent == fromContent && - replacedTransition.toContent == toContent) - ) - } - - /** - * Force this transition to finish and animate to an [Idle] state. - * - * Important: Once this is called, the effective state of the transition should remain - * unchanged. For instance, in the case of a [TransitionState.Transition], its - * [currentScene][TransitionState.Transition.currentScene] should never change once [finish] - * is called. - * - * @return the [Job] that animates to the idle state. It can be used to wait until the - * animation is complete or cancel it to snap the animation. Calling [finish] multiple - * times will return the same [Job]. - */ - internal abstract fun finish(): Job - - /** - * Whether we are transitioning. If [from] or [to] is empty, we will also check that they - * match the contents we are animating from and/or to. - */ - fun isTransitioning(from: ContentKey? = null, to: ContentKey? = null): Boolean { - return (from == null || fromContent == from) && (to == null || toContent == to) - } - - /** Whether we are transitioning from [content] to [other], or from [other] to [content]. */ - fun isTransitioningBetween(content: ContentKey, other: ContentKey): Boolean { - return isTransitioning(from = content, to = other) || - isTransitioning(from = other, to = content) - } - - internal fun updateOverscrollSpecs( - fromSpec: OverscrollSpecImpl?, - toSpec: OverscrollSpecImpl?, - ) { - fromOverscrollSpec = fromSpec - toOverscrollSpec = toSpec - } - - /** Returns if the [progress] value of this transition can go beyond range `[0; 1]` */ - internal fun isWithinProgressRange(progress: Float): Boolean { - // If the properties are missing we assume that every [Transition] can overscroll - if (this !is HasOverscrollProperties) return true - // [OverscrollSpec] for the current scene, even if it hasn't started overscrolling yet. - val specForCurrentScene = - when { - progress <= 0f -> fromOverscrollSpec - progress >= 1f -> toOverscrollSpec - else -> null - } ?: return true - - return specForCurrentScene.transformationSpec.transformations.isNotEmpty() - } - - internal open fun interruptionProgress( - layoutImpl: SceneTransitionLayoutImpl, - ): Float { - if (!layoutImpl.state.enableInterruptions) { - return 0f - } - - if (replacedTransition != null) { - return replacedTransition.interruptionProgress(layoutImpl) - } - - fun create(): Animatable<Float, AnimationVector1D> { - val animatable = Animatable(1f, visibilityThreshold = ProgressVisibilityThreshold) - layoutImpl.coroutineScope.launch { - val swipeSpec = layoutImpl.state.transitions.defaultSwipeSpec - val progressSpec = - spring( - stiffness = swipeSpec.stiffness, - dampingRatio = swipeSpec.dampingRatio, - visibilityThreshold = ProgressVisibilityThreshold, - ) - animatable.animateTo(0f, progressSpec) - } - - return animatable - } - - val animatable = interruptionDecay ?: create().also { interruptionDecay = it } - return animatable.value - } - } - - interface HasOverscrollProperties { - /** - * The position of the [Transition.toContent]. - * - * Used to understand the direction of the overscroll. - */ - val isUpOrLeft: Boolean - - /** - * The relative orientation between [Transition.fromContent] and [Transition.toContent]. - * - * Used to understand the orientation of the overscroll. - */ - val orientation: Orientation - - /** - * Scope which can be used in the Overscroll DSL to define a transformation based on the - * distance between [Transition.fromContent] and [Transition.toContent]. - */ - val overscrollScope: OverscrollScope - - /** - * The content (scene or overlay) around which the transition is currently bouncing. When - * not `null`, this transition is currently oscillating around this content and will soon - * settle to that content. - */ - val bouncingContent: ContentKey? - - companion object { - const val DistanceUnspecified = 0f - } - } -} 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 77de22ceeec4..22df34b34b97 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 @@ -16,16 +16,31 @@ package com.android.compose.animation.scene.content.state +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.AnimationVector1D +import androidx.compose.animation.core.spring +import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.Stable +import com.android.compose.animation.scene.ContentKey +import com.android.compose.animation.scene.OverscrollScope +import com.android.compose.animation.scene.OverscrollSpecImpl +import com.android.compose.animation.scene.ProgressVisibilityThreshold import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.SceneTransitionLayoutImpl +import com.android.compose.animation.scene.TransformationSpec +import com.android.compose.animation.scene.TransformationSpecImpl +import com.android.compose.animation.scene.TransitionKey +import com.android.compose.animation.scene.transition.link.LinkedTransition +import com.android.compose.animation.scene.transition.link.StateLink +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch -/** The state associated to one or more scenes. */ -// TODO(b/353679003): Rename to SceneState. +/** The state associated to a [SceneTransitionLayout] at some specific point in time. */ @Stable -sealed interface TransitionState : ContentState<SceneKey> { +sealed interface TransitionState { /** - * The current effective scene. If a new transition was triggered, it would start from this - * scene. + * The current effective scene. If a new scene transition was triggered, it would start from + * this scene. * * For instance, when swiping from scene A to scene B, the [currentScene] is A when the swipe * gesture starts, but then if the user flings their finger and commits the transition to scene @@ -37,17 +52,216 @@ sealed interface TransitionState : ContentState<SceneKey> { /** The scene [currentScene] is idle. */ data class Idle( override val currentScene: SceneKey, - ) : TransitionState, ContentState.Idle<SceneKey>(currentScene) + ) : TransitionState - /** There is a transition animating between [fromScene] and [toScene]. */ - abstract class Transition( - /** The scene this transition is starting from. Can't be the same as toScene */ - val fromScene: SceneKey, + sealed class Transition( + val fromContent: ContentKey, + val toContent: ContentKey, + val replacedTransition: Transition? = null, + ) : TransitionState { + /** A transition animating between [fromScene] and [toScene]. */ + abstract class ChangeCurrentScene( + /** The scene this transition is starting from. Can't be the same as toScene */ + val fromScene: SceneKey, - /** The scene this transition is going to. Can't be the same as fromScene */ - val toScene: SceneKey, + /** The scene this transition is going to. Can't be the same as fromScene */ + val toScene: SceneKey, - /** The transition that `this` transition is replacing, if any. */ - replacedTransition: Transition? = null, - ) : TransitionState, ContentState.Transition<SceneKey>(fromScene, toScene, replacedTransition) + /** The transition that `this` transition is replacing, if any. */ + replacedTransition: Transition? = null, + ) : Transition(fromScene, toScene, replacedTransition) + + /** + * The key of this transition. This should usually be null, but it can be specified to use a + * specific set of transformations associated to this transition. + */ + open val key: TransitionKey? = null + + /** + * The progress of the transition. This is usually in the `[0; 1]` range, but it can also be + * less than `0` or greater than `1` when using transitions with a spring AnimationSpec or + * when flinging quickly during a swipe gesture. + */ + abstract val progress: Float + + /** The current velocity of [progress], in progress units. */ + abstract val progressVelocity: Float + + /** Whether the transition was triggered by user input rather than being programmatic. */ + abstract val isInitiatedByUserInput: Boolean + + /** Whether user input is currently driving the transition. */ + abstract val isUserInputOngoing: Boolean + + /** + * The progress of the preview transition. This is usually in the `[0; 1]` range, but it can + * also be less than `0` or greater than `1` when using transitions with a spring + * AnimationSpec or when flinging quickly during a swipe gesture. + */ + internal open val previewProgress: Float = 0f + + /** The current velocity of [previewProgress], in progress units. */ + internal open val previewProgressVelocity: Float = 0f + + /** Whether the transition is currently in the preview stage */ + internal open val isInPreviewStage: Boolean = false + + /** + * The current [TransformationSpecImpl] and [OverscrollSpecImpl] associated to this + * transition. + * + * Important: These will be set exactly once, when this transition is + * [started][MutableSceneTransitionLayoutStateImpl.startTransition]. + */ + internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty + internal var previewTransformationSpec: TransformationSpecImpl? = null + private var fromOverscrollSpec: OverscrollSpecImpl? = null + private var toOverscrollSpec: OverscrollSpecImpl? = null + + /** The current [OverscrollSpecImpl], if this transition is currently overscrolling. */ + internal val currentOverscrollSpec: OverscrollSpecImpl? + get() { + if (this !is HasOverscrollProperties) return null + val progress = progress + val bouncingContent = bouncingContent + return when { + progress < 0f || bouncingContent == fromContent -> fromOverscrollSpec + progress > 1f || bouncingContent == toContent -> toOverscrollSpec + else -> null + } + } + + /** + * An animatable that animates from 1f to 0f. This will be used to nicely animate the sudden + * jump of values when this transitions interrupts another one. + */ + private var interruptionDecay: Animatable<Float, AnimationVector1D>? = null + + /** The map of active links that connects this transition to other transitions. */ + internal val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>() + + init { + check(fromContent != toContent) + check( + replacedTransition == null || + (replacedTransition.fromContent == fromContent && + replacedTransition.toContent == toContent) + ) + } + + /** + * Whether we are transitioning. If [from] or [to] is empty, we will also check that they + * match the contents we are animating from and/or to. + */ + fun isTransitioning(from: ContentKey? = null, to: ContentKey? = null): Boolean { + return (from == null || fromContent == from) && (to == null || toContent == to) + } + + /** Whether we are transitioning from [content] to [other], or from [other] to [content]. */ + fun isTransitioningBetween(content: ContentKey, other: ContentKey): Boolean { + return isTransitioning(from = content, to = other) || + isTransitioning(from = other, to = content) + } + + /** + * Force this transition to finish and animate to an [Idle] state. + * + * Important: Once this is called, the effective state of the transition should remain + * unchanged. For instance, in the case of a [TransitionState.Transition], its + * [currentScene][TransitionState.Transition.currentScene] should never change once [finish] + * is called. + * + * @return the [Job] that animates to the idle state. It can be used to wait until the + * animation is complete or cancel it to snap the animation. Calling [finish] multiple + * times will return the same [Job]. + */ + internal abstract fun finish(): Job + + internal fun updateOverscrollSpecs( + fromSpec: OverscrollSpecImpl?, + toSpec: OverscrollSpecImpl?, + ) { + fromOverscrollSpec = fromSpec + toOverscrollSpec = toSpec + } + + /** Returns if the [progress] value of this transition can go beyond range `[0; 1]` */ + internal fun isWithinProgressRange(progress: Float): Boolean { + // If the properties are missing we assume that every [Transition] can overscroll + if (this !is HasOverscrollProperties) return true + // [OverscrollSpec] for the current scene, even if it hasn't started overscrolling yet. + val specForCurrentScene = + when { + progress <= 0f -> fromOverscrollSpec + progress >= 1f -> toOverscrollSpec + else -> null + } ?: return true + + return specForCurrentScene.transformationSpec.transformations.isNotEmpty() + } + + internal open fun interruptionProgress( + layoutImpl: SceneTransitionLayoutImpl, + ): Float { + if (!layoutImpl.state.enableInterruptions) { + return 0f + } + + if (replacedTransition != null) { + return replacedTransition.interruptionProgress(layoutImpl) + } + + fun create(): Animatable<Float, AnimationVector1D> { + val animatable = Animatable(1f, visibilityThreshold = ProgressVisibilityThreshold) + layoutImpl.coroutineScope.launch { + val swipeSpec = layoutImpl.state.transitions.defaultSwipeSpec + val progressSpec = + spring( + stiffness = swipeSpec.stiffness, + dampingRatio = swipeSpec.dampingRatio, + visibilityThreshold = ProgressVisibilityThreshold, + ) + animatable.animateTo(0f, progressSpec) + } + + return animatable + } + + val animatable = interruptionDecay ?: create().also { interruptionDecay = it } + return animatable.value + } + } + + interface HasOverscrollProperties { + /** + * The position of the [Transition.toContent]. + * + * Used to understand the direction of the overscroll. + */ + val isUpOrLeft: Boolean + + /** + * The relative orientation between [Transition.fromContent] and [Transition.toContent]. + * + * Used to understand the orientation of the overscroll. + */ + val orientation: Orientation + + /** + * Scope which can be used in the Overscroll DSL to define a transformation based on the + * distance between [Transition.fromContent] and [Transition.toContent]. + */ + val overscrollScope: OverscrollScope + + /** + * The content (scene or overlay) around which the transition is currently bouncing. When + * not `null`, this transition is currently oscillating around this content and will soon + * settle to that content. + */ + val bouncingContent: ContentKey? + + companion object { + const val DistanceUnspecified = 0f + } + } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt index 538ce799c3eb..c5a3067cc4d9 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt @@ -22,7 +22,7 @@ import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.ElementMatcher import com.android.compose.animation.scene.SceneTransitionLayoutImpl -import com.android.compose.animation.scene.content.state.ContentState +import com.android.compose.animation.scene.content.state.TransitionState /** Anchor the size of an element to the size of another element. */ internal class AnchoredSize( @@ -36,7 +36,7 @@ internal class AnchoredSize( content: ContentKey, element: Element, stateInContent: Element.State, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, value: IntSize, ): IntSize { fun anchorSizeIn(content: ContentKey): IntSize { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt index 258f54122711..05878c2d3f08 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt @@ -23,7 +23,7 @@ import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.ElementMatcher import com.android.compose.animation.scene.SceneTransitionLayoutImpl -import com.android.compose.animation.scene.content.state.ContentState +import com.android.compose.animation.scene.content.state.TransitionState /** Anchor the translation of an element to another element. */ internal class AnchoredTranslate( @@ -35,7 +35,7 @@ internal class AnchoredTranslate( content: ContentKey, element: Element, stateInContent: Element.State, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, value: Offset, ): Offset { fun throwException(content: ContentKey?): Nothing { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt index be8dac21a5cf..7f8647976873 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt @@ -22,7 +22,7 @@ import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher import com.android.compose.animation.scene.Scale import com.android.compose.animation.scene.SceneTransitionLayoutImpl -import com.android.compose.animation.scene.content.state.ContentState +import com.android.compose.animation.scene.content.state.TransitionState /** * Scales the draw size of an element. Note this will only scale the draw inside of an element, @@ -40,7 +40,7 @@ internal class DrawScale( content: ContentKey, element: Element, stateInContent: Element.State, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, value: Scale, ): Scale { return Scale(scaleX, scaleY, pivot) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt index d72e43abe8f4..a32c7dd09f95 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt @@ -22,7 +22,7 @@ import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher import com.android.compose.animation.scene.SceneTransitionLayoutImpl -import com.android.compose.animation.scene.content.state.ContentState +import com.android.compose.animation.scene.content.state.TransitionState /** Translate an element from an edge of the layout. */ internal class EdgeTranslate( @@ -35,7 +35,7 @@ internal class EdgeTranslate( content: ContentKey, element: Element, stateInContent: Element.State, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, value: Offset ): Offset { val sceneSize = layoutImpl.content(content).targetSize diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt index 92ae30f8e3db..4528eefedb3e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt @@ -20,7 +20,7 @@ import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher import com.android.compose.animation.scene.SceneTransitionLayoutImpl -import com.android.compose.animation.scene.content.state.ContentState +import com.android.compose.animation.scene.content.state.TransitionState /** Fade an element in or out. */ internal class Fade( @@ -31,7 +31,7 @@ internal class Fade( content: ContentKey, element: Element, stateInContent: Element.State, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, value: Float ): Float { // Return the alpha value of [element] either when it starts fading in or when it finished diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt index e8515dce5fec..5f3fdaf67e5f 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt @@ -21,7 +21,7 @@ import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher import com.android.compose.animation.scene.SceneTransitionLayoutImpl -import com.android.compose.animation.scene.content.state.ContentState +import com.android.compose.animation.scene.content.state.TransitionState import kotlin.math.roundToInt /** @@ -38,7 +38,7 @@ internal class ScaleSize( content: ContentKey, element: Element, stateInContent: Element.State, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, value: IntSize, ): IntSize { return IntSize( diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt index eda8edeceeb9..505ad04c598c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt @@ -25,7 +25,7 @@ import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher import com.android.compose.animation.scene.SceneTransitionLayoutImpl -import com.android.compose.animation.scene.content.state.ContentState +import com.android.compose.animation.scene.content.state.TransitionState /** A transformation applied to one or more elements during a transition. */ sealed interface Transformation { @@ -66,7 +66,7 @@ internal sealed interface PropertyTransformation<T> : Transformation { content: ContentKey, element: Element, stateInContent: Element.State, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, value: T, ): T } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt index fab4ced1ff44..59bca50f7d5b 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt @@ -24,7 +24,7 @@ import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher import com.android.compose.animation.scene.OverscrollScope import com.android.compose.animation.scene.SceneTransitionLayoutImpl -import com.android.compose.animation.scene.content.state.ContentState +import com.android.compose.animation.scene.content.state.TransitionState internal class Translate( override val matcher: ElementMatcher, @@ -36,7 +36,7 @@ internal class Translate( content: ContentKey, element: Element, stateInContent: Element.State, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, value: Offset, ): Offset { return with(layoutImpl.density) { @@ -58,13 +58,13 @@ internal class OverscrollTranslate( content: ContentKey, element: Element, stateInContent: Element.State, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, value: Offset, ): Offset { // As this object is created by OverscrollBuilderImpl and we retrieve the current // OverscrollSpec only when the transition implements HasOverscrollProperties, we can assume // that this method was invoked after performing this check. - val overscrollProperties = transition as ContentState.HasOverscrollProperties + val overscrollProperties = transition as TransitionState.HasOverscrollProperties return Offset( x = value.x + overscrollProperties.overscrollScope.x(), diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt index 23bcf109eb98..89b004046475 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt @@ -23,11 +23,11 @@ import kotlinx.coroutines.Job /** A linked transition which is driven by a [originalTransition]. */ internal class LinkedTransition( - private val originalTransition: TransitionState.Transition, + private val originalTransition: TransitionState.Transition.ChangeCurrentScene, fromScene: SceneKey, toScene: SceneKey, override val key: TransitionKey? = null, -) : TransitionState.Transition(fromScene, toScene) { +) : TransitionState.Transition.ChangeCurrentScene(fromScene, toScene) { override val currentScene: SceneKey get() { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt index c0c40dd23897..c29bf212ec9c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt @@ -49,7 +49,9 @@ class StateLink(target: SceneTransitionLayoutState, val transitionLinks: List<Tr error("From and To can't be the same") } - internal fun isMatchingLink(transition: TransitionState.Transition): Boolean { + internal fun isMatchingLink( + transition: TransitionState.Transition.ChangeCurrentScene, + ): Boolean { return (sourceFrom == null || sourceFrom == transition.fromScene) && (sourceTo == null || sourceTo == transition.toScene) } 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 6360f852de57..72a16b7fbd6f 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 @@ -182,7 +182,7 @@ class DraggableHandlerTest { progress: Float? = null, isUserInputOngoing: Boolean? = null ): Transition { - val transition = assertThat(transitionState).isTransition() + val transition = assertThat(transitionState).isSceneTransition() currentScene?.let { assertThat(transition).hasCurrentScene(it) } fromScene?.let { assertThat(transition).hasFromScene(it) } toScene?.let { assertThat(transition).hasToScene(it) } @@ -1075,7 +1075,7 @@ class DraggableHandlerTest { val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f) val dragController = onDragStarted(startedPosition = middle, overSlop = up(0.5f)) - val transition = assertThat(transitionState).isTransition() + val transition = assertThat(transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) assertThat(transition).hasProgress(0.5f) @@ -1101,7 +1101,7 @@ class DraggableHandlerTest { val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f) val dragController = onDragStarted(startedPosition = middle, overSlop = down(0.5f)) - val transition = assertThat(transitionState).isTransition() + val transition = assertThat(transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneC) assertThat(transition).hasProgress(0.5f) @@ -1127,7 +1127,7 @@ class DraggableHandlerTest { val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f) val dragController = onDragStarted(startedPosition = middle, overSlop = up(1.5f)) - val transition = assertThat(transitionState).isTransition() + val transition = assertThat(transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) assertThat(transition).hasProgress(1.5f) @@ -1154,7 +1154,7 @@ class DraggableHandlerTest { val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f) val dragController = onDragStarted(startedPosition = middle, overSlop = down(1.5f)) - val transition = assertThat(transitionState).isTransition() + val transition = assertThat(transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneC) assertThat(transition).hasProgress(1.5f) @@ -1182,7 +1182,7 @@ class DraggableHandlerTest { val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f) val dragController = onDragStarted(startedPosition = middle, overSlop = down(1f)) - val transition = assertThat(transitionState).isTransition() + val transition = assertThat(transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) assertThat(transition).hasProgress(-1f) @@ -1210,7 +1210,7 @@ class DraggableHandlerTest { val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f) val dragController = onDragStarted(startedPosition = middle, overSlop = up(1f)) - val transition = assertThat(transitionState).isTransition() + val transition = assertThat(transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneC) assertThat(transition).hasProgress(-1f) @@ -1267,12 +1267,12 @@ class DraggableHandlerTest { @Test fun interceptingTransitionReplacesCurrentTransition() = runGestureTest { val controller = onDragStarted(overSlop = up(fractionOfScreen = 0.5f)) - val transition = assertThat(layoutState.transitionState).isTransition() + val transition = assertThat(layoutState.transitionState).isSceneTransition() controller.onDragStopped(velocity = 0f) // Intercept the transition. onDragStartedImmediately() - val newTransition = assertThat(layoutState.transitionState).isTransition() + val newTransition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(newTransition).isNotSameInstanceAs(transition) assertThat(newTransition.replacedTransition).isSameInstanceAs(transition) } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt index 20b9b49c21d7..682fe95c66a5 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt @@ -735,7 +735,7 @@ class ElementTest { val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag) fooElement.assertTopPositionInRootIsEqualTo(0.dp) - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).isNotNull() assertThat(transition).hasProgress(0.5f) assertThat(animatedFloat).isEqualTo(50f) @@ -822,7 +822,7 @@ class ElementTest { moveBy(Offset(0f, touchSlop + layoutHeight.toPx() * 0.5f), delayMillis = 1_000) } - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasOverscrollSpec() assertThat(transition).hasProgress(-0.5f) fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 0.5f) @@ -905,7 +905,7 @@ class ElementTest { } } - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasProgress(0.5f) fooElement.assertTopPositionInRootIsEqualTo(translateY * 0.5f) } @@ -939,7 +939,7 @@ class ElementTest { moveBy(Offset(0f, layoutHeight.toPx() * 0.5f), delayMillis = 1_000) } - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(animatedFloat).isEqualTo(100f) // Scroll 150% (100% scroll + 50% overscroll) @@ -992,7 +992,7 @@ class ElementTest { moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000) } - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(animatedFloat).isEqualTo(100f) // Scroll 200% (100% scroll + 100% overscroll) @@ -1039,7 +1039,7 @@ class ElementTest { moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000) } - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(animatedFloat).isEqualTo(100f) // Scroll 200% (100% scroll + 100% overscroll) @@ -1083,7 +1083,7 @@ class ElementTest { moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000) } - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(animatedFloat).isEqualTo(100f) // Scroll 200% (100% scroll + 100% overscroll) @@ -1143,7 +1143,7 @@ class ElementTest { moveBy(Offset(0f, layoutHeight.toPx() * 0.5f), delayMillis = 1_000) } - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() // Scroll 150% (100% scroll + 50% overscroll) assertThat(transition).hasProgress(1.5f) @@ -1160,7 +1160,7 @@ class ElementTest { assertThat(transition.progress).isLessThan(1f) assertThat(transition).hasOverscrollSpec() - assertThat(transition).hasBouncingScene(transition.toScene) + assertThat(transition).hasBouncingContent(transition.toContent) assertThat(animatedFloat).isEqualTo(100f) } @@ -1243,13 +1243,15 @@ class ElementTest { val transitions = state.currentTransitions assertThat(transitions).hasSize(2) - assertThat(transitions[0]).hasFromScene(SceneA) - assertThat(transitions[0]).hasToScene(SceneB) - assertThat(transitions[0]).hasProgress(0f) - - assertThat(transitions[1]).hasFromScene(SceneB) - assertThat(transitions[1]).hasToScene(SceneC) - assertThat(transitions[1]).hasProgress(0f) + val firstTransition = assertThat(transitions[0]).isSceneTransition() + assertThat(firstTransition).hasFromScene(SceneA) + assertThat(firstTransition).hasToScene(SceneB) + assertThat(firstTransition).hasProgress(0f) + + val secondTransition = assertThat(transitions[1]).isSceneTransition() + assertThat(secondTransition).hasFromScene(SceneB) + assertThat(secondTransition).hasToScene(SceneC) + assertThat(secondTransition).hasProgress(0f) // First frame: both are at x = 0dp. For the whole transition, Foo is at y = 0dp and Bar is // at y = layoutSize - elementSoze = 100dp. diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt index ca72181e4fcf..f4e60a2a4100 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt @@ -69,7 +69,7 @@ class InterruptionHandlerTest { interruptionHandler = object : InterruptionHandler { override fun onInterruption( - interrupted: TransitionState.Transition, + interrupted: TransitionState.Transition.ChangeCurrentScene, newTargetScene: SceneKey ): InterruptionResult { return InterruptionResult( @@ -104,7 +104,7 @@ class InterruptionHandlerTest { interruptionHandler = object : InterruptionHandler { override fun onInterruption( - interrupted: TransitionState.Transition, + interrupted: TransitionState.Transition.ChangeCurrentScene, newTargetScene: SceneKey ): InterruptionResult { return InterruptionResult( @@ -198,7 +198,7 @@ class InterruptionHandlerTest { companion object { val FromToCurrentTriple = Correspondence.transforming( - { transition: TransitionState.Transition? -> + { transition: TransitionState.Transition.ChangeCurrentScene? -> Triple(transition?.fromScene, transition?.toScene, transition?.currentScene) }, "(from, to, current) triple" diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt index 520e7599f56a..b7f50fd8d685 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt @@ -45,7 +45,6 @@ import androidx.compose.ui.unit.dp import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compose.animation.scene.TestScenes.SceneA import com.android.compose.animation.scene.TestScenes.SceneB -import com.android.compose.animation.scene.content.state.ContentState import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.subjects.assertThat import com.android.compose.test.assertSizeIsEqualTo @@ -160,11 +159,11 @@ class MovableElementTest { override fun contentDuringTransition( element: ElementKey, - transition: ContentState.Transition<*>, + transition: TransitionState.Transition, fromContentZIndex: Float, toContentZIndex: Float ): ContentKey { - transition as TransitionState.Transition + transition as TransitionState.Transition.ChangeCurrentScene assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) assertThat(fromContentZIndex).isEqualTo(0) diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt index ccefe3d38937..d58a0a3cc0aa 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt @@ -125,7 +125,7 @@ class NestedScrollToSceneTest { scrollUp(percent = 0.5f) // STL will start a transition with the remaining scroll - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasProgress(0.5f) scrollUp(percent = 1f) @@ -156,7 +156,7 @@ class NestedScrollToSceneTest { // Start a new gesture pointerDownAndScrollTouchSlop() scrollUp(percent = 0.5f) - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasProgress(0.5f) pointerUp() @@ -181,7 +181,7 @@ class NestedScrollToSceneTest { // Reach the end of the scrollable element canScroll = false scrollUp(percent = 0.5f) - val transition1 = assertThat(state.transitionState).isTransition() + val transition1 = assertThat(state.transitionState).isSceneTransition() assertThat(transition1).hasProgress(0.5f) pointerUp() @@ -192,7 +192,7 @@ class NestedScrollToSceneTest { // Start a new gesture pointerDownAndScrollTouchSlop() scrollUp(percent = 0.5f) - val transition2 = assertThat(state.transitionState).isTransition() + val transition2 = assertThat(state.transitionState).isSceneTransition() assertThat(transition2).hasProgress(0.5f) pointerUp() @@ -215,7 +215,7 @@ class NestedScrollToSceneTest { // Reach the end of the scrollable element canScroll = false scrollUp(percent = 0.5f) - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasProgress(0.5f) pointerUp() @@ -244,7 +244,7 @@ class NestedScrollToSceneTest { scrollUp(percent = 0.5f) // EdgeAlways always consume the remaining scroll, EdgeNoPreview does not. - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasProgress(0.5f) } @@ -278,7 +278,7 @@ class NestedScrollToSceneTest { scrollUp(percent = 0.2f) // STL can only start the transition if it has reset the amount of scroll consumed. - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasProgress(0.2f) } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt index 0eaecb09e97e..00c75882a587 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt @@ -76,7 +76,7 @@ class PredictiveBackHandlerTest { dispatcher.dispatchOnBackProgressed(backEvent(progress = 0.4f)) } - val transition = assertThat(layoutState.transitionState).isTransition() + val transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) assertThat(transition).hasProgress(0.4f) @@ -124,7 +124,7 @@ class PredictiveBackHandlerTest { dispatcher.dispatchOnBackProgressed(backEvent(progress = 0.4f)) } - val transition = assertThat(layoutState.transitionState).isTransition() + val transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) assertThat(transition).hasPreviewProgress(0.4f) @@ -178,13 +178,13 @@ class PredictiveBackHandlerTest { val dispatcher = rule.activity.onBackPressedDispatcher rule.runOnUiThread { dispatcher.dispatchOnBackStarted(backEvent()) } - val predictiveTransition = assertThat(layoutState.transitionState).isTransition() + val predictiveTransition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(predictiveTransition).hasFromScene(SceneA) assertThat(predictiveTransition).hasToScene(SceneB) // Start a new transition to C. rule.runOnUiThread { layoutState.setTargetScene(SceneC, coroutineScope) } - val newTransition = assertThat(layoutState.transitionState).isTransition() + val newTransition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(newTransition).hasFromScene(SceneA) assertThat(newTransition).hasToScene(SceneC) diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt index c8ac580bf5ba..3422a8e47a3d 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt @@ -478,7 +478,7 @@ class SceneTransitionLayoutStateTest { overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) } } ) - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasNoOverscrollSpec() // overscroll for SceneA is NOT defined @@ -510,7 +510,7 @@ class SceneTransitionLayoutStateTest { } ) - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasNoOverscrollSpec() // overscroll for SceneA is defined @@ -539,7 +539,7 @@ class SceneTransitionLayoutStateTest { sceneTransitions = transitions {} ) - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasNoOverscrollSpec() // overscroll for SceneA is NOT defined @@ -642,7 +642,7 @@ class SceneTransitionLayoutStateTest { // Transition to B. state.setTargetScene(SceneB, coroutineScope = this) - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasCurrentScene(SceneB) // Snap to C. diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt index 84bcc28f5b1e..e97c27e5a034 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt @@ -179,7 +179,7 @@ class SceneTransitionLayoutTest { // Change the current scene. currentScene = SceneB - val transition = assertThat(layoutState.transitionState).isTransition() + val transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) assertThat(transition).hasProgress(0f) @@ -241,7 +241,7 @@ class SceneTransitionLayoutTest { // 100.dp. We pause at the middle of the transition, so it should now be 75.dp given that we // use a linear interpolator. Foo was at (x = layoutSize - 50dp, y = 0) in SceneA and is // going to (x = 0, y = 0), so the offset should now be half what it was. - var transition = assertThat(layoutState.transitionState).isTransition() + var transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasProgress(0.5f) sharedFoo.assertWidthIsEqualTo(75.dp) sharedFoo.assertHeightIsEqualTo(75.dp) @@ -269,7 +269,7 @@ class SceneTransitionLayoutTest { val expectedSize = 100.dp + (150.dp - 100.dp) * interpolatedProgress sharedFoo = rule.onNode(isElement(TestElements.Foo, SceneC)) - transition = assertThat(layoutState.transitionState).isTransition() + transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasProgress(interpolatedProgress) sharedFoo.assertWidthIsEqualTo(expectedSize) sharedFoo.assertHeightIsEqualTo(expectedSize) @@ -399,7 +399,7 @@ class SceneTransitionLayoutTest { rule.mainClock.advanceTimeBy(duration / 2) rule.waitForIdle() - var transition = assertThat(state.transitionState).isTransition() + var transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasProgress(0.5f) // A and B are composed. @@ -412,7 +412,7 @@ class SceneTransitionLayoutTest { rule.mainClock.advanceTimeByFrame() rule.waitForIdle() - transition = assertThat(state.transitionState).isTransition() + transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasProgress(0f) // A, B and C are composed. diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 04a938055308..06799bcda0ef 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -155,7 +155,7 @@ class SwipeToSceneTest { // We should be at a progress = 55dp / LayoutWidth given that we use the layout size in // the gesture axis as swipe distance. - var transition = assertThat(layoutState.transitionState).isTransition() + var transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) assertThat(transition).hasCurrentScene(SceneA) @@ -165,7 +165,7 @@ class SwipeToSceneTest { // Release the finger. We should now be animating back to A (currentScene = SceneA) given // that 55dp < positional threshold. rule.onRoot().performTouchInput { up() } - transition = assertThat(layoutState.transitionState).isTransition() + transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) assertThat(transition).hasCurrentScene(SceneA) @@ -185,7 +185,7 @@ class SwipeToSceneTest { } // Drag is in progress, so currentScene = SceneA and progress = 56dp / LayoutHeight - transition = assertThat(layoutState.transitionState).isTransition() + transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(TestScenes.SceneC) assertThat(transition).hasCurrentScene(SceneA) @@ -195,7 +195,7 @@ class SwipeToSceneTest { // Release the finger. We should now be animating to C (currentScene = SceneC) given // that 56dp >= positional threshold. rule.onRoot().performTouchInput { up() } - transition = assertThat(layoutState.transitionState).isTransition() + transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(TestScenes.SceneC) assertThat(transition).hasCurrentScene(TestScenes.SceneC) @@ -236,7 +236,7 @@ class SwipeToSceneTest { // We should be animating back to A (currentScene = SceneA) given that 124 dp/s < velocity // threshold. - var transition = assertThat(layoutState.transitionState).isTransition() + var transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) assertThat(transition).hasCurrentScene(SceneA) @@ -260,7 +260,7 @@ class SwipeToSceneTest { } // We should be animating to C (currentScene = SceneC). - transition = assertThat(layoutState.transitionState).isTransition() + transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(TestScenes.SceneC) assertThat(transition).hasCurrentScene(TestScenes.SceneC) @@ -297,7 +297,7 @@ class SwipeToSceneTest { } // We are transitioning to B because we used 2 fingers. - val transition = assertThat(layoutState.transitionState).isTransition() + val transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(TestScenes.SceneC) assertThat(transition).hasToScene(SceneB) @@ -332,7 +332,7 @@ class SwipeToSceneTest { } // We are transitioning to B (and not A) because we started from the top edge. - var transition = assertThat(layoutState.transitionState).isTransition() + var transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(TestScenes.SceneC) assertThat(transition).hasToScene(SceneB) @@ -350,7 +350,7 @@ class SwipeToSceneTest { } // We are transitioning to B (and not A) because we started from the left edge. - transition = assertThat(layoutState.transitionState).isTransition() + transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(TestScenes.SceneC) assertThat(transition).hasToScene(SceneB) @@ -406,7 +406,7 @@ class SwipeToSceneTest { } // We should be at 50% - val transition = assertThat(layoutState.transitionState).isTransition() + val transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).isNotNull() assertThat(transition).hasProgress(0.5f) } @@ -427,7 +427,7 @@ class SwipeToSceneTest { } // We should still correctly compute that we are swiping down to scene C. - var transition = assertThat(layoutState.transitionState).isTransition() + var transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasToScene(TestScenes.SceneC) // Release the finger, animating back to scene A. @@ -443,7 +443,7 @@ class SwipeToSceneTest { } // We should still correctly compute that we are swiping up to scene B. - transition = assertThat(layoutState.transitionState).isTransition() + transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasToScene(SceneB) // Release the finger, animating back to scene A. @@ -459,7 +459,7 @@ class SwipeToSceneTest { } // We should still correctly compute that we are swiping down to scene B. - transition = assertThat(layoutState.transitionState).isTransition() + transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasToScene(SceneB) } @@ -597,7 +597,7 @@ class SwipeToSceneTest { } rule.waitForIdle() - val transition = assertThat(state.transitionState).isTransition() + val transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) assertThat(transition).hasProgress(0.5f, tolerance = 0.01f) @@ -686,7 +686,7 @@ class SwipeToSceneTest { } // Scene B should come from the right (end) edge. - var transition = assertThat(state.transitionState).isTransition() + var transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) rule @@ -707,7 +707,7 @@ class SwipeToSceneTest { } // Scene C should come from the left (start) edge. - transition = assertThat(state.transitionState).isTransition() + transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneC) rule @@ -761,7 +761,7 @@ class SwipeToSceneTest { } // Scene C should come from the right (start) edge. - var transition = assertThat(state.transitionState).isTransition() + var transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneC) rule @@ -782,7 +782,7 @@ class SwipeToSceneTest { } // Scene C should come from the left (end) edge. - transition = assertThat(state.transitionState).isTransition() + transition = assertThat(state.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) rule diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt index e4e410828d0a..1f7fe3766971 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt @@ -17,7 +17,6 @@ package com.android.compose.animation.scene import androidx.compose.foundation.gestures.Orientation -import com.android.compose.animation.scene.content.state.ContentState import com.android.compose.animation.scene.content.state.TransitionState import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -43,10 +42,10 @@ fun transition( orientation: Orientation = Orientation.Horizontal, onFinish: ((TransitionState.Transition) -> Job)? = null, replacedTransition: TransitionState.Transition? = null, -): TransitionState.Transition { +): TransitionState.Transition.ChangeCurrentScene { return object : - TransitionState.Transition(from, to, replacedTransition), - ContentState.HasOverscrollProperties { + TransitionState.Transition.ChangeCurrentScene(from, to, replacedTransition), + TransitionState.HasOverscrollProperties { override val currentScene: SceneKey get() = current() 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 a12ab7888a15..a98bd7652c4e 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 @@ -16,9 +16,9 @@ package com.android.compose.animation.scene.subjects +import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.OverscrollSpec import com.android.compose.animation.scene.SceneKey -import com.android.compose.animation.scene.content.state.ContentState import com.android.compose.animation.scene.content.state.TransitionState import com.google.common.truth.Fact.simpleFact import com.google.common.truth.FailureMetadata @@ -31,9 +31,9 @@ fun assertThat(state: TransitionState): TransitionStateSubject { return Truth.assertAbout(TransitionStateSubject.transitionStates()).that(state) } -/** Assert on a [TransitionState.Transition]. */ -fun assertThat(transitions: TransitionState.Transition): TransitionSubject { - return Truth.assertAbout(TransitionSubject.transitions()).that(transitions) +/** Assert on a [TransitionState.Transition.ChangeCurrentScene]. */ +fun assertThat(transition: TransitionState.Transition.ChangeCurrentScene): SceneTransitionSubject { + return Truth.assertAbout(SceneTransitionSubject.transitions()).that(transition) } class TransitionStateSubject @@ -53,12 +53,14 @@ private constructor( return actual as TransitionState.Idle } - fun isTransition(): TransitionState.Transition { - if (actual !is TransitionState.Transition) { - failWithActual(simpleFact("expected to be TransitionState.Transition")) + fun isSceneTransition(): TransitionState.Transition.ChangeCurrentScene { + if (actual !is TransitionState.Transition.ChangeCurrentScene) { + failWithActual( + simpleFact("expected to be TransitionState.Transition.ChangeCurrentScene") + ) } - return actual as TransitionState.Transition + return actual as TransitionState.Transition.ChangeCurrentScene } companion object { @@ -68,10 +70,10 @@ private constructor( } } -class TransitionSubject +class SceneTransitionSubject private constructor( metadata: FailureMetadata, - private val actual: TransitionState.Transition, + private val actual: TransitionState.Transition.ChangeCurrentScene, ) : Subject(metadata, actual) { fun hasCurrentScene(sceneKey: SceneKey) { check("currentScene").that(actual.currentScene).isEqualTo(sceneKey) @@ -132,19 +134,21 @@ private constructor( check("currentOverscrollSpec").that(actual.currentOverscrollSpec).isNull() } - fun hasBouncingScene(scene: SceneKey) { - if (actual !is ContentState.HasOverscrollProperties) { + fun hasBouncingContent(content: ContentKey) { + val actual = actual + if (actual !is TransitionState.HasOverscrollProperties) { failWithActual(simpleFact("expected to be ContentState.HasOverscrollProperties")) } check("bouncingContent") - .that((actual as ContentState.HasOverscrollProperties).bouncingContent) - .isEqualTo(scene) + .that((actual as TransitionState.HasOverscrollProperties).bouncingContent) + .isEqualTo(content) } companion object { - fun transitions() = Factory { metadata, actual: TransitionState.Transition -> - TransitionSubject(metadata, actual) - } + fun transitions() = + Factory { metadata, actual: TransitionState.Transition.ChangeCurrentScene -> + SceneTransitionSubject(metadata, actual) + } } } |