summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt110
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt (renamed from packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaScenePicker.kt)36
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt5
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt8
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt30
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt12
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt1
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt1
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt12
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt186
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt2
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt32
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt15
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt1
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt1
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt2
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt225
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt16
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt247
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt10
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt3
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt237
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt53
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt19
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt27
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt8
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt12
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt2
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt2
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt15
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt3
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementContentPickerTest.kt (renamed from packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementScenePickerTest.kt)6
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt4
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt1
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt (renamed from packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt)37
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt64
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt1
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt8
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt2
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt11
53 files changed, 879 insertions, 651 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index cce06007385b..d4bad23a1ee9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -36,7 +36,7 @@ import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.LowestZIndexScenePicker
+import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneScope
@@ -62,7 +62,7 @@ import kotlin.time.DurationUnit
object Communal {
object Elements {
- val Scrim = ElementKey("Scrim", scenePicker = LowestZIndexScenePicker)
+ val Scrim = ElementKey("Scrim", contentPicker = LowestZIndexContentPicker)
val Grid = ElementKey("CommunalContent")
val LockIcon = ElementKey("CommunalLockIcon")
val IndicationArea = ElementKey("CommunalIndicationArea")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 6feaf6d8ceec..9c72d933da32 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -71,7 +71,7 @@ constructor(
applyPadding: Boolean,
modifier: Modifier = Modifier,
) {
- MovableElement(
+ Element(
key = if (isStart) StartButtonElementKey else EndButtonElementKey,
modifier = modifier,
) {
@@ -98,7 +98,7 @@ constructor(
fun SceneScope.IndicationArea(
modifier: Modifier = Modifier,
) {
- MovableElement(
+ Element(
key = IndicationAreaElementKey,
modifier = modifier.indicationAreaPadding(),
) {
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 218779da20b9..bcdb259f161f 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
@@ -120,7 +120,7 @@ constructor(
)
}
- MovableElement(key = largeClockElementKey, modifier = modifier) {
+ Element(key = largeClockElementKey, modifier = modifier) {
content {
AndroidView(
factory = { context ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
index 44bda956b9f6..33ed14b2e7cc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
@@ -65,68 +65,74 @@ constructor(
) {
val resources = LocalContext.current.resources
- MovableElement(key = ClockElementKeys.smartspaceElementKey, modifier = modifier) {
- Column(
- modifier =
- modifier
- .onTopPlacementChanged(onTopChanged)
- .padding(
- top = { lockscreenContentViewModel.getSmartSpacePaddingTop(resources) },
- bottom = {
- resources.getDimensionPixelSize(
- R.dimen.keyguard_status_view_bottom_margin
- )
- }
- )
- ) {
- if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
- return@Column
- }
+ Element(key = ClockElementKeys.smartspaceElementKey, modifier = modifier) {
+ content {
+ Column(
+ modifier =
+ modifier
+ .onTopPlacementChanged(onTopChanged)
+ .padding(
+ top = {
+ lockscreenContentViewModel.getSmartSpacePaddingTop(resources)
+ },
+ bottom = {
+ resources.getDimensionPixelSize(
+ R.dimen.keyguard_status_view_bottom_margin
+ )
+ }
+ )
+ ) {
+ if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
+ return@Column
+ }
- val paddingBelowClockStart = dimensionResource(R.dimen.below_clock_padding_start)
- val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
+ val paddingBelowClockStart =
+ dimensionResource(R.dimen.below_clock_padding_start)
+ val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
- if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
- Row(
- verticalAlignment = Alignment.CenterVertically,
+ if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier =
+ Modifier.fillMaxWidth()
+ // All items will be constrained to be as tall as the shortest
+ // item.
+ .height(IntrinsicSize.Min)
+ .padding(
+ start = paddingBelowClockStart,
+ ),
+ ) {
+ Date(
+ modifier =
+ Modifier.burnInAware(
+ viewModel = aodBurnInViewModel,
+ params = burnInParams,
+ ),
+ )
+ Spacer(modifier = Modifier.width(4.dp))
+ Weather(
+ modifier =
+ Modifier.burnInAware(
+ viewModel = aodBurnInViewModel,
+ params = burnInParams,
+ ),
+ )
+ }
+ }
+
+ Card(
modifier =
Modifier.fillMaxWidth()
- // All items will be constrained to be as tall as the shortest item.
- .height(IntrinsicSize.Min)
.padding(
start = paddingBelowClockStart,
- ),
- ) {
- Date(
- modifier =
- Modifier.burnInAware(
- viewModel = aodBurnInViewModel,
- params = burnInParams,
- ),
- )
- Spacer(modifier = Modifier.width(4.dp))
- Weather(
- modifier =
- Modifier.burnInAware(
+ end = paddingBelowClockEnd,
+ )
+ .burnInAware(
viewModel = aodBurnInViewModel,
params = burnInParams,
),
- )
- }
+ )
}
-
- Card(
- modifier =
- Modifier.fillMaxWidth()
- .padding(
- start = paddingBelowClockStart,
- end = paddingBelowClockEnd,
- )
- .burnInAware(
- viewModel = aodBurnInViewModel,
- params = burnInParams,
- ),
- )
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
index 9a82da251b7f..2e39524baaad 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
@@ -128,7 +128,7 @@ constructor(
elementKey: ElementKey,
modifier: Modifier = Modifier,
) {
- MovableElement(key = elementKey, modifier) {
+ Element(key = elementKey, modifier) {
content {
AndroidView(
factory = {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index f8bd633d99a6..26ab10b459d8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -28,7 +28,7 @@ import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.viewinterop.AndroidView
-import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.MovableElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
import com.android.systemui.media.controls.ui.view.MediaHost
@@ -38,7 +38,10 @@ import com.android.systemui.util.animation.MeasurementInput
object MediaCarousel {
object Elements {
internal val Content =
- ElementKey(debugName = "MediaCarouselContent", scenePicker = MediaScenePicker)
+ MovableElementKey(
+ debugName = "MediaCarouselContent",
+ contentPicker = MediaContentPicker,
+ )
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaScenePicker.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt
index 7b497e84db62..3f04f3728bef 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaScenePicker.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt
@@ -16,18 +16,19 @@
package com.android.systemui.media.controls.ui.composable
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.ElementContentPicker
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.ElementScenePicker
-import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayoutState
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.StaticElementContentPicker
+import com.android.compose.animation.scene.content.state.ContentState
import com.android.systemui.scene.shared.model.Scenes
-/** [ElementScenePicker] implementation for the media carousel object. */
-object MediaScenePicker : ElementScenePicker {
+/** [ElementContentPicker] implementation for the media carousel object. */
+object MediaContentPicker : StaticElementContentPicker {
const val SHADE_FRACTION = 0.66f
- private val scenes =
+ override val contents =
setOf(
Scenes.Lockscreen,
Scenes.Shade,
@@ -36,12 +37,12 @@ object MediaScenePicker : ElementScenePicker {
Scenes.Communal
)
- override fun sceneDuringTransition(
+ override fun contentDuringTransition(
element: ElementKey,
- transition: TransitionState.Transition,
- fromSceneZIndex: Float,
- toSceneZIndex: Float
- ): SceneKey? {
+ transition: ContentState.Transition<*>,
+ fromContentZIndex: Float,
+ toContentZIndex: Float
+ ): ContentKey {
return when {
shouldElevateMedia(transition) -> {
Scenes.Shade
@@ -52,22 +53,23 @@ object MediaScenePicker : ElementScenePicker {
transition.isTransitioningBetween(Scenes.QuickSettings, Scenes.Shade) -> {
Scenes.QuickSettings
}
+ transition.toContent in contents -> transition.toContent
else -> {
- when {
- scenes.contains(transition.toScene) -> transition.toScene
- scenes.contains(transition.fromScene) -> transition.fromScene
- else -> null
+ check(transition.fromContent in contents) {
+ "Media player should not be composed for the transition from " +
+ "${transition.fromContent} to ${transition.toContent}"
}
+ transition.fromContent
}
}
}
/** Returns true when the media should be laid on top of the rest for the given [transition]. */
- fun shouldElevateMedia(transition: TransitionState.Transition): Boolean {
+ fun shouldElevateMedia(transition: ContentState.Transition<*>): Boolean {
return transition.isTransitioningBetween(Scenes.Lockscreen, Scenes.Shade)
}
}
-fun MediaScenePicker.shouldElevateMedia(layoutState: SceneTransitionLayoutState): Boolean {
+fun MediaContentPicker.shouldElevateMedia(layoutState: SceneTransitionLayoutState): Boolean {
return layoutState.currentTransition?.let { shouldElevateMedia(it) } ?: false
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 2eb7b3f89af5..84782fdfc0af 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -78,7 +78,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.LowestZIndexScenePicker
+import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.animation.scene.NestedScrollBehavior
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.thenIf
@@ -105,7 +105,7 @@ object Notifications {
val NotificationScrim = ElementKey("NotificationScrim")
val NotificationStackPlaceholder = ElementKey("NotificationStackPlaceholder")
val HeadsUpNotificationPlaceholder =
- ElementKey("HeadsUpNotificationPlaceholder", scenePicker = LowestZIndexScenePicker)
+ ElementKey("HeadsUpNotificationPlaceholder", contentPicker = LowestZIndexContentPicker)
val ShelfSpace = ElementKey("ShelfSpace")
val NotificationStackCutoffGuideline = ElementKey("NotificationStackCutoffGuideline")
}
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 8058dcde8cf8..f3994360ea18 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
@@ -33,10 +33,11 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.MovableElementScenePicker
+import com.android.compose.animation.scene.MovableElementContentPicker
+import com.android.compose.animation.scene.MovableElementKey
import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.TransitionState
import com.android.compose.animation.scene.ValueKey
+import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.modifiers.thenIf
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
@@ -55,7 +56,10 @@ object QuickSettings {
object Elements {
val Content =
- ElementKey("QuickSettingsContent", scenePicker = MovableElementScenePicker(SCENES))
+ MovableElementKey(
+ "QuickSettingsContent",
+ contentPicker = MovableElementContentPicker(SCENES)
+ )
val QuickQuickSettings = ElementKey("QuickQuickSettings")
val SplitShadeQuickSettings = ElementKey("SplitShadeQuickSettings")
val FooterActions = ElementKey("QuickSettingsFooterActions")
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 2800eee661c6..cdcd840b2f34 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
@@ -68,11 +68,11 @@ import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.TransitionState
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.animateSceneDpAsState
import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.modifiers.thenIf
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.battery.BatteryMeterViewController
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
index 2f8c24826376..a9da733116fc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
@@ -25,7 +25,7 @@ import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.UserActionDistance
import com.android.compose.animation.scene.UserActionDistanceScope
import com.android.systemui.media.controls.ui.composable.MediaCarousel
-import com.android.systemui.media.controls.ui.composable.MediaScenePicker
+import com.android.systemui.media.controls.ui.composable.MediaContentPicker
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.shade.ui.composable.Shade
@@ -54,7 +54,8 @@ fun TransitionBuilder.goneToSplitShadeTransition(
fractionRange(end = .33f) { fade(Shade.Elements.BackgroundScrim) }
fractionRange(start = .33f) {
- val qsTranslation = ShadeHeader.Dimensions.CollapsedHeight * MediaScenePicker.SHADE_FRACTION
+ val qsTranslation =
+ ShadeHeader.Dimensions.CollapsedHeight * MediaContentPicker.SHADE_FRACTION
val qsExpansionDiff =
ShadeHeader.Dimensions.ExpandedHeight - ShadeHeader.Dimensions.CollapsedHeight
translate(MediaCarousel.Elements.Content, y = -(qsExpansionDiff + qsTranslation))
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
index 7d46c7570dac..21dfc49cbe7b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
@@ -26,7 +26,7 @@ import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.UserActionDistance
import com.android.compose.animation.scene.UserActionDistanceScope
import com.android.systemui.media.controls.ui.composable.MediaCarousel
-import com.android.systemui.media.controls.ui.composable.MediaScenePicker
+import com.android.systemui.media.controls.ui.composable.MediaContentPicker
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.scene.shared.model.Scenes
@@ -62,7 +62,7 @@ fun TransitionBuilder.toShadeTransition(
fade(QuickSettings.Elements.FooterActions)
}
- val qsTranslation = ShadeHeader.Dimensions.CollapsedHeight * MediaScenePicker.SHADE_FRACTION
+ val qsTranslation = ShadeHeader.Dimensions.CollapsedHeight * MediaContentPicker.SHADE_FRACTION
val qsExpansionDiff =
ShadeHeader.Dimensions.ExpandedHeight - ShadeHeader.Dimensions.CollapsedHeight
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 865622326fcd..facbcaffcb5a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -49,7 +49,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.LowestZIndexScenePicker
+import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.animation.scene.SceneScope
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.keyguard.ui.composable.LockscreenContent
@@ -186,10 +186,10 @@ private fun combinePaddings(vararg paddingValues: PaddingValues): PaddingValues
object OverlayShade {
object Elements {
- val Scrim = ElementKey("OverlayShadeScrim", scenePicker = LowestZIndexScenePicker)
- val Panel = ElementKey("OverlayShadePanel", scenePicker = LowestZIndexScenePicker)
+ val Scrim = ElementKey("OverlayShadeScrim", contentPicker = LowestZIndexContentPicker)
+ val Panel = ElementKey("OverlayShadePanel", contentPicker = LowestZIndexContentPicker)
val PanelBackground =
- ElementKey("OverlayShadePanelBackground", scenePicker = LowestZIndexScenePicker)
+ ElementKey("OverlayShadePanelBackground", contentPicker = LowestZIndexContentPicker)
}
object Colors {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index b5a10ca1e478..1cd48bf2e628 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -59,11 +59,11 @@ import androidx.compose.ui.unit.max
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.LowestZIndexScenePicker
+import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.TransitionState
import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateElementFloatAsState
+import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.modifiers.thenIf
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.settingslib.Utils
@@ -93,8 +93,8 @@ object ShadeHeader {
val ExpandedContent = ElementKey("ShadeHeaderExpandedContent")
val CollapsedContentStart = ElementKey("ShadeHeaderCollapsedContentStart")
val CollapsedContentEnd = ElementKey("ShadeHeaderCollapsedContentEnd")
- val PrivacyChip = ElementKey("PrivacyChip", scenePicker = LowestZIndexScenePicker)
- val Clock = ElementKey("ShadeHeaderClock", scenePicker = LowestZIndexScenePicker)
+ val PrivacyChip = ElementKey("PrivacyChip", contentPicker = LowestZIndexContentPicker)
+ val Clock = ElementKey("ShadeHeaderClock", contentPicker = LowestZIndexContentPicker)
val ShadeCarrierGroup = ElementKey("ShadeCarrierGroup")
}
@@ -110,6 +110,7 @@ object ShadeHeader {
object Colors {
val ColorScheme.shadeHeaderText: Color
get() = Color.White
+
val ColorScheme.onScrimDim: Color
get() = Color.DarkGray
}
@@ -148,8 +149,8 @@ fun SceneScope.CollapsedShadeHeader(
}
val isLargeScreenLayout =
- LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Medium ||
- LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Expanded
+ LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Medium ||
+ LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Expanded
val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
@@ -197,8 +198,8 @@ fun SceneScope.CollapsedShadeHeader(
) {
if (isLargeScreenLayout) {
ShadeCarrierGroup(
- viewModel = viewModel,
- modifier = Modifier.align(Alignment.CenterVertically),
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.CenterVertically),
)
}
SystemIconContainer(
@@ -552,19 +553,20 @@ private fun SystemIconContainer(
val interactionSource = remember { MutableInteractionSource() }
val isHovered by interactionSource.collectIsHoveredAsState()
- val hoverModifier = Modifier
- .clip(RoundedCornerShape(CollapsedHeight / 4))
+ val hoverModifier =
+ Modifier.clip(RoundedCornerShape(CollapsedHeight / 4))
.background(MaterialTheme.colorScheme.onScrimDim)
Row(
- modifier = modifier
+ modifier =
+ modifier
.height(CollapsedHeight)
.padding(vertical = CollapsedHeight / 4)
.thenIf(isClickable) {
Modifier.clickable(
- interactionSource = interactionSource,
- indication = null,
- onClick = { viewModel.onSystemIconContainerClicked() },
+ interactionSource = interactionSource,
+ indication = null,
+ onClick = { viewModel.onSystemIconContainerClicked() },
)
}
.thenIf(isHovered) { hoverModifier },
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 18ca0f75245e..77b48d3d307e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -63,14 +63,14 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.LowestZIndexScenePicker
+import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.animation.scene.NestedScrollBehavior
import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.TransitionState
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.animateSceneDpAsState
import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.modifiers.padding
import com.android.compose.modifiers.thenIf
import com.android.compose.windowsizeclass.LocalWindowSizeClass
@@ -81,7 +81,7 @@ import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadi
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.media.controls.ui.composable.MediaCarousel
-import com.android.systemui.media.controls.ui.composable.MediaScenePicker
+import com.android.systemui.media.controls.ui.composable.MediaContentPicker
import com.android.systemui.media.controls.ui.composable.shouldElevateMedia
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
@@ -118,7 +118,7 @@ object Shade {
object Elements {
val MediaCarousel = ElementKey("ShadeMediaCarousel")
val BackgroundScrim =
- ElementKey("ShadeBackgroundScrim", scenePicker = LowestZIndexScenePicker)
+ ElementKey("ShadeBackgroundScrim", contentPicker = LowestZIndexContentPicker)
val SplitShadeStartColumn = ElementKey("SplitShadeStartColumn")
}
@@ -376,7 +376,7 @@ private fun SceneScope.SingleShade(
layout(constraints.maxWidth, constraints.maxHeight) {
val qsZIndex =
- if (MediaScenePicker.shouldElevateMedia(layoutState)) {
+ if (MediaContentPicker.shouldElevateMedia(layoutState)) {
1f
} else {
0f
@@ -563,7 +563,7 @@ private fun SceneScope.SplitShade(
mediaHost = mediaHost,
modifier =
Modifier.fillMaxWidth().thenIf(
- MediaScenePicker.shouldElevateMedia(layoutState)
+ MediaContentPicker.shouldElevateMedia(layoutState)
) {
Modifier.zIndex(1f)
},
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 afbc8e71c940..e13ca39149f3 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
@@ -32,6 +32,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastCoerceIn
import androidx.compose.ui.util.fastLastOrNull
+import com.android.compose.animation.scene.content.state.TransitionState
import kotlin.math.roundToInt
/**
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 82c85d1fa38b..1fc1f989b095 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
@@ -19,6 +19,7 @@ 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.TransitionState
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
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 0f7e3eaf75ad..a43028a340f4 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
@@ -30,8 +30,10 @@ import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastCoerceIn
-import com.android.compose.animation.scene.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
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.nestedscroll.PriorityNestedScrollConnection
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
@@ -608,7 +610,7 @@ private class SwipeTransition(
var lastDistance: Float = DistanceUnspecified,
) :
TransitionState.Transition(_fromScene.key, _toScene.key, replacedTransition),
- TransitionState.HasOverscrollProperties {
+ ContentState.HasOverscrollProperties {
var _currentScene by mutableStateOf(_fromScene)
override val currentScene: SceneKey
get() = _currentScene.key
@@ -645,7 +647,7 @@ private class SwipeTransition(
override val isInitiatedByUserInput = true
- override var bouncingScene: SceneKey? = null
+ override var bouncingContent: SceneKey? = null
/** The current offset caused by the drag gesture. */
var dragOffset by mutableFloatStateOf(0f)
@@ -764,7 +766,7 @@ private class SwipeTransition(
animationSpec = swipeSpec,
initialVelocity = initialVelocity,
) {
- if (bouncingScene == null) {
+ if (bouncingContent == null) {
val isBouncing =
if (isTargetGreater) {
if (startedWhenOvercrollingTargetScene) {
@@ -781,7 +783,7 @@ private class SwipeTransition(
}
if (isBouncing) {
- bouncingScene = targetScene
+ bouncingContent = targetScene
// Immediately stop this transition if we are bouncing on a
// scene that does not bounce.
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 0b5e58faf1db..ec7c77bdb3b7 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,13 +49,15 @@ 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
import com.android.compose.ui.util.lerp
import kotlin.math.roundToInt
import kotlinx.coroutines.launch
-/** An element on screen, that can be composed in one or more scenes. */
+/** An element on screen, that can be composed in one or more contents. */
@Stable
internal class Element(val key: ElementKey) {
/** The mapping between a content and the state this element has in that content, if any. */
@@ -67,7 +69,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: TransitionState.Transition? = null
+ var lastTransition: ContentState.Transition<*>? = null
/** Whether this element was ever drawn in a content. */
var wasDrawnInAnyContent = false
@@ -86,7 +88,7 @@ internal class Element(val key: ElementKey) {
var targetSize by mutableStateOf(SizeUnspecified)
var targetOffset by mutableStateOf(Offset.Unspecified)
- /** The last state this element had in this scene. */
+ /** The last state this element had in this content. */
var lastOffset = Offset.Unspecified
var lastSize = SizeUnspecified
var lastScale = Scale.Unspecified
@@ -103,7 +105,7 @@ internal class Element(val key: ElementKey) {
/**
* The delta values to add to this element state to have smoother interruptions. These
* should be multiplied by the
- * [current interruption progress][TransitionState.Transition.interruptionProgress] so that
+ * [current interruption progress][ContentState.Transition.interruptionProgress] so that
* they nicely animate from their values down to 0.
*/
var offsetInterruptionDelta = Offset.Zero
@@ -310,7 +312,7 @@ internal class ElementNode(
}
private fun Placeable.PlacementScope.place(
- transition: TransitionState.Transition?,
+ transition: ContentState.Transition<*>?,
placeable: Placeable,
) {
with(layoutImpl.lookaheadScope) {
@@ -475,7 +477,7 @@ private fun elementTransition(
layoutImpl: SceneTransitionLayoutImpl,
element: Element,
transitions: List<TransitionState.Transition>,
-): TransitionState.Transition? {
+): ContentState.Transition<*>? {
val transition =
transitions.fastLastOrNull { transition ->
transition.fromScene in element.stateByContent ||
@@ -502,8 +504,8 @@ private fun elementTransition(
private fun prepareInterruption(
layoutImpl: SceneTransitionLayoutImpl,
element: Element,
- transition: TransitionState.Transition,
- previousTransition: TransitionState.Transition,
+ transition: ContentState.Transition<*>,
+ previousTransition: ContentState.Transition<*>,
) {
if (transition.replacedTransition == previousTransition) {
return
@@ -514,10 +516,10 @@ private fun prepareInterruption(
return stateByContent[key]?.also { it.selfUpdateValuesBeforeInterruption() }
}
- val previousFromState = updateStateInContent(previousTransition.fromScene)
- val previousToState = updateStateInContent(previousTransition.toScene)
- val fromState = updateStateInContent(transition.fromScene)
- val toState = updateStateInContent(transition.toScene)
+ val previousFromState = updateStateInContent(previousTransition.fromContent)
+ val previousToState = updateStateInContent(previousTransition.toContent)
+ val fromState = updateStateInContent(transition.fromContent)
+ val toState = updateStateInContent(transition.toContent)
reconcileStates(element, previousTransition)
reconcileStates(element, transition)
@@ -545,31 +547,31 @@ private fun prepareInterruption(
}
/**
- * Reconcile the state of [element] in the fromScene and toScene of [transition] so that the values
- * before interruption have their expected values, taking shared transitions into account.
+ * Reconcile the state of [element] in the formContent and toContent of [transition] so that the
+ * values before interruption have their expected values, taking shared transitions into account.
*/
private fun reconcileStates(
element: Element,
- transition: TransitionState.Transition,
+ transition: ContentState.Transition<*>,
) {
- val fromSceneState = element.stateByContent[transition.fromScene] ?: return
- val toSceneState = element.stateByContent[transition.toScene] ?: return
+ val fromContentState = element.stateByContent[transition.fromContent] ?: return
+ val toContentState = element.stateByContent[transition.toContent] ?: return
if (!isSharedElementEnabled(element.key, transition)) {
return
}
if (
- fromSceneState.offsetBeforeInterruption != Offset.Unspecified &&
- toSceneState.offsetBeforeInterruption == Offset.Unspecified
+ fromContentState.offsetBeforeInterruption != Offset.Unspecified &&
+ toContentState.offsetBeforeInterruption == Offset.Unspecified
) {
- // Element is shared and placed in fromScene only.
- toSceneState.updateValuesBeforeInterruption(fromSceneState)
+ // Element is shared and placed in fromContent only.
+ toContentState.updateValuesBeforeInterruption(fromContentState)
} else if (
- toSceneState.offsetBeforeInterruption != Offset.Unspecified &&
- fromSceneState.offsetBeforeInterruption == Offset.Unspecified
+ toContentState.offsetBeforeInterruption != Offset.Unspecified &&
+ fromContentState.offsetBeforeInterruption == Offset.Unspecified
) {
- // Element is shared and placed in toScene only.
- fromSceneState.updateValuesBeforeInterruption(toSceneState)
+ // Element is shared and placed in toContent only.
+ fromContentState.updateValuesBeforeInterruption(toContentState)
}
}
@@ -614,12 +616,12 @@ private fun Element.State.clearValuesBeforeInterruption() {
/**
* Compute what [value] should be if we take the
- * [interruption progress][TransitionState.Transition.interruptionProgress] of [transition] into
+ * [interruption progress][ContentState.Transition.interruptionProgress] of [transition] into
* account.
*/
private inline fun <T> computeInterruptedValue(
layoutImpl: SceneTransitionLayoutImpl,
- transition: TransitionState.Transition?,
+ transition: ContentState.Transition<*>?,
value: T,
unspecifiedValue: T,
zeroValue: T,
@@ -660,13 +662,13 @@ private inline fun <T> computeInterruptedValue(
/**
* Set the interruption delta of a *placement/drawing*-related value (offset, alpha, scale). This
- * ensures that the delta is also set on the other scene in the transition for shared elements, so
- * that there is no jump cut if the scene where the element is placed has changed.
+ * ensures that the delta is also set on the other content in the transition for shared elements, so
+ * that there is no jump cut if the content where the element is placed has changed.
*/
private inline fun <T> setPlacementInterruptionDelta(
element: Element,
stateInContent: Element.State,
- transition: TransitionState.Transition?,
+ transition: ContentState.Transition<*>?,
delta: T,
setter: (Element.State, T) -> Unit,
) {
@@ -677,14 +679,14 @@ private inline fun <T> setPlacementInterruptionDelta(
return
}
- // If the element is shared, also set the delta on the other scene so that it is used by that
- // scene if we start overscrolling it and change the scene where the element is placed.
- val otherScene =
- if (stateInContent.content == transition.fromScene) transition.toScene
- else transition.fromScene
- val otherSceneState = element.stateByContent[otherScene] ?: return
+ // If the element is shared, also set the delta on the other content so that it is used by that
+ // content if we start overscrolling it and change the content where the element is placed.
+ val otherContent =
+ if (stateInContent.content == transition.fromContent) transition.toContent
+ else transition.fromContent
+ val otherContentState = element.stateByContent[otherContent] ?: return
if (isSharedElementEnabled(element.key, transition)) {
- setter(otherSceneState, delta)
+ setter(otherContentState, delta)
}
}
@@ -692,7 +694,7 @@ private fun shouldPlaceElement(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: Element,
- transition: TransitionState.Transition?,
+ transition: ContentState.Transition<*>?,
): Boolean {
// Always place the element if we are idle.
if (transition == null) {
@@ -701,14 +703,14 @@ private fun shouldPlaceElement(
// Don't place the element in this content if this content is not part of the current element
// transition.
- if (content != transition.fromScene && content != transition.toScene) {
+ if (content != transition.fromContent && content != transition.toContent) {
return false
}
// Place the element if it is not shared.
if (
- transition.fromScene !in element.stateByContent ||
- transition.toScene !in element.stateByContent
+ transition.fromContent !in element.stateByContent ||
+ transition.toContent !in element.stateByContent
) {
return true
}
@@ -730,7 +732,7 @@ internal fun shouldPlaceOrComposeSharedElement(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: ElementKey,
- transition: TransitionState.Transition,
+ transition: ContentState.Transition<*>,
): Boolean {
// If we are overscrolling, only place/compose the element in the overscrolling scene.
val overscrollScene = transition.currentOverscrollSpec?.scene
@@ -738,45 +740,47 @@ internal fun shouldPlaceOrComposeSharedElement(
return content == overscrollScene
}
- val scenePicker = element.scenePicker
- val fromScene = transition.fromScene
- val toScene = transition.toScene
-
+ val scenePicker = element.contentPicker
val pickedScene =
- scenePicker.sceneDuringTransition(
- element = element,
- transition = transition,
- fromSceneZIndex = layoutImpl.scenes.getValue(fromScene).zIndex,
- toSceneZIndex = layoutImpl.scenes.getValue(toScene).zIndex,
- ) ?: return false
+ when (transition) {
+ is TransitionState.Transition -> {
+ scenePicker.contentDuringTransition(
+ element = element,
+ transition = transition,
+ fromContentZIndex = layoutImpl.scene(transition.fromScene).zIndex,
+ toContentZIndex = layoutImpl.scene(transition.toScene).zIndex,
+ )
+ }
+ }
return pickedScene == content
}
private fun isSharedElementEnabled(
element: ElementKey,
- transition: TransitionState.Transition,
+ transition: ContentState.Transition<*>,
): Boolean {
return sharedElementTransformation(element, transition)?.enabled ?: true
}
internal fun sharedElementTransformation(
element: ElementKey,
- transition: TransitionState.Transition,
+ transition: ContentState.Transition<*>,
): SharedElementTransformation? {
val transformationSpec = transition.transformationSpec
- val sharedInFromScene = transformationSpec.transformations(element, transition.fromScene).shared
- val sharedInToScene = transformationSpec.transformations(element, transition.toScene).shared
+ val sharedInFromContent =
+ transformationSpec.transformations(element, transition.fromContent).shared
+ val sharedInToContent = transformationSpec.transformations(element, transition.toContent).shared
- // The sharedElement() transformation must either be null or be the same in both scenes.
- if (sharedInFromScene != sharedInToScene) {
+ // The sharedElement() transformation must either be null or be the same in both contents.
+ if (sharedInFromContent != sharedInToContent) {
error(
- "Different sharedElement() transformations matched $element (from=$sharedInFromScene " +
- "to=$sharedInToScene)"
+ "Different sharedElement() transformations matched $element " +
+ "(from=$sharedInFromContent to=$sharedInToContent)"
)
}
- return sharedInFromScene
+ return sharedInFromContent
}
/**
@@ -789,16 +793,14 @@ internal fun sharedElementTransformation(
private fun isElementOpaque(
content: Content,
element: Element,
- transition: TransitionState.Transition?,
+ transition: ContentState.Transition<*>?,
): Boolean {
if (transition == null) {
return true
}
- val fromScene = transition.fromScene
- val toScene = transition.toScene
- val fromState = element.stateByContent[fromScene]
- val toState = element.stateByContent[toScene]
+ val fromState = element.stateByContent[transition.fromContent]
+ val toState = element.stateByContent[transition.toContent]
if (fromState == null && toState == null) {
// TODO(b/311600838): Throw an exception instead once layers of disposed elements are not
@@ -825,7 +827,7 @@ private fun isElementOpaque(
private fun elementAlpha(
layoutImpl: SceneTransitionLayoutImpl,
element: Element,
- transition: TransitionState.Transition?,
+ transition: ContentState.Transition<*>?,
stateInContent: Element.State,
): Float {
val alpha =
@@ -856,7 +858,7 @@ private fun elementAlpha(
private fun interruptedAlpha(
layoutImpl: SceneTransitionLayoutImpl,
element: Element,
- transition: TransitionState.Transition?,
+ transition: ContentState.Transition<*>?,
stateInContent: Element.State,
alpha: Float,
): Float {
@@ -886,7 +888,7 @@ private fun interruptedAlpha(
private fun measure(
layoutImpl: SceneTransitionLayoutImpl,
element: Element,
- transition: TransitionState.Transition?,
+ transition: ContentState.Transition<*>?,
stateInContent: Element.State,
measurable: Measurable,
constraints: Constraints,
@@ -950,7 +952,7 @@ private fun Placeable.size(): IntSize = IntSize(width, height)
private fun ContentDrawScope.getDrawScale(
layoutImpl: SceneTransitionLayoutImpl,
element: Element,
- transition: TransitionState.Transition?,
+ transition: ContentState.Transition<*>?,
stateInContent: Element.State,
): Scale {
val scale =
@@ -1031,7 +1033,7 @@ private fun ContentDrawScope.getDrawScale(
* @param layoutImpl the [SceneTransitionLayoutImpl] associated to [element].
* @param currentContentState the content state of the content for which we are computing the value.
* Note that during interruptions, this could be the state of a content that is neither
- * [transition.toScene] nor [transition.fromScene].
+ * [transition.toContent] nor [transition.fromContent].
* @param element the element being animated.
* @param contentValue the value being animated.
* @param transformation the transformation associated to the value being animated.
@@ -1046,7 +1048,7 @@ private inline fun <T> computeValue(
layoutImpl: SceneTransitionLayoutImpl,
currentContentState: Element.State,
element: Element,
- transition: TransitionState.Transition?,
+ transition: ContentState.Transition<*>?,
contentValue: (Element.State) -> T,
transformation: (ElementTransformations) -> PropertyTransformation<T>?,
currentValue: () -> T,
@@ -1061,11 +1063,11 @@ private inline fun <T> computeValue(
return currentValue()
}
- val fromScene = transition.fromScene
- val toScene = transition.toScene
+ val fromContent = transition.fromContent
+ val toContent = transition.toContent
- val fromState = element.stateByContent[fromScene]
- val toState = element.stateByContent[toScene]
+ val fromState = element.stateByContent[fromContent]
+ val toState = element.stateByContent[toContent]
if (fromState == null && toState == null) {
// TODO(b/311600838): Throw an exception instead once layers of disposed elements are not
@@ -1073,19 +1075,20 @@ private inline fun <T> computeValue(
return contentValue(currentContentState)
}
- val currentScene = currentContentState.content
- if (transition is TransitionState.HasOverscrollProperties) {
+ val currentContent = currentContentState.content
+ if (transition is ContentState.HasOverscrollProperties) {
val overscroll = transition.currentOverscrollSpec
- if (overscroll?.scene == currentScene) {
+ if (overscroll?.scene == currentContent) {
val elementSpec =
- overscroll.transformationSpec.transformations(element.key, currentScene)
+ overscroll.transformationSpec.transformations(element.key, currentContent)
val propertySpec = transformation(elementSpec) ?: return currentValue()
- val overscrollState = checkNotNull(if (currentScene == toScene) toState else fromState)
+ val overscrollState =
+ checkNotNull(if (currentContent == toContent) toState else fromState)
val idleValue = contentValue(overscrollState)
val targetValue =
propertySpec.transform(
layoutImpl,
- currentScene,
+ currentContent,
element,
overscrollState,
transition,
@@ -1101,8 +1104,8 @@ private inline fun <T> computeValue(
// TODO(b/290184746): Make sure that we don't overflow transformations associated to a
// range.
val directionSign = if (transition.isUpOrLeft) -1 else 1
- val isToScene = overscroll.scene == transition.toScene
- val linearProgress = transition.progress.let { if (isToScene) it - 1f else it }
+ val isToContent = overscroll.scene == transition.toContent
+ val linearProgress = transition.progress.let { if (isToContent) it - 1f else it }
val progress = directionSign * overscroll.progressConverter(linearProgress)
val rangeProgress = propertySpec.range?.progress(progress) ?: progress
@@ -1111,7 +1114,8 @@ private inline fun <T> computeValue(
}
}
- // The element is shared: interpolate between the value in fromScene and the value in toScene.
+ // The element is shared: interpolate between the value in fromContent and the value in
+ // toContent.
// TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared
// elements follow the finger direction.
val isSharedElement = fromState != null && toState != null
@@ -1134,15 +1138,15 @@ private inline fun <T> computeValue(
val contentState =
checkNotNull(
when {
- isSharedElement && currentScene == fromScene -> fromState
+ isSharedElement && currentContent == fromContent -> fromState
isSharedElement -> toState
else -> fromState ?: toState
}
)
- // The scene for which we compute the transformation. Note that this is not necessarily
- // [currentScene] because [currentScene] could be a different scene than the transition
- // fromScene or toScene during interruptions.
+ // The content for which we compute the transformation. Note that this is not necessarily
+ // [currentContent] because [currentContent] could be a different content than the transition
+ // fromContent or toContent during interruptions.
val content = contentState.content
val transformation =
@@ -1156,7 +1160,7 @@ private inline fun <T> computeValue(
val isInPreviewStage = transition.isInPreviewStage
val idleValue = contentValue(contentState)
- val isEntering = content == toScene
+ val isEntering = content == toContent
val previewTargetValue =
previewTransformation.transform(
layoutImpl,
@@ -1262,7 +1266,7 @@ private inline fun <T> computeValue(
val rangeProgress = transformation.range?.progress(progress) ?: progress
// Interpolate between the value at rest and the value before entering/after leaving.
- val isEntering = content == toScene
+ val isEntering = content == toContent
return if (isEntering) {
lerp(targetValue, idleValue, rangeProgress)
} else {
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 54c64fd721ec..bf70ca9b6744 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
@@ -16,6 +16,8 @@
package com.android.compose.animation.scene
+import com.android.compose.animation.scene.content.state.TransitionState
+
/**
* A handler to specify how a transition should be interrupted.
*
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
index a9edf0afc66f..3cd5553bc963 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
@@ -64,15 +64,15 @@ class SceneKey(
}
/** Key for an element. */
-class ElementKey(
+open class ElementKey(
debugName: String,
identity: Any = Object(),
/**
- * The [ElementScenePicker] to use when deciding in which scene we should draw shared Elements
+ * The [ElementContentPicker] to use when deciding in which scene we should draw shared Elements
* or compose MovableElements.
*/
- val scenePicker: ElementScenePicker = DefaultElementScenePicker,
+ open val contentPicker: ElementContentPicker = DefaultElementContentPicker,
) : Key(debugName, identity), ElementMatcher {
@VisibleForTesting
// TODO(b/240432457): Make internal once PlatformComposeSceneTransitionLayoutTestsUtils can
@@ -99,6 +99,32 @@ class ElementKey(
}
}
+/** Key for a movable element. */
+class MovableElementKey(
+ debugName: String,
+
+ /**
+ * The [StaticElementContentPicker] to use when deciding in which scene we should draw shared
+ * Elements or compose MovableElements.
+ *
+ * @see MovableElementContentPicker
+ */
+ override val contentPicker: StaticElementContentPicker,
+ identity: Any = Object(),
+) : ElementKey(debugName, identity, contentPicker) {
+ constructor(
+ debugName: String,
+
+ /** The exhaustive list of contents (scenes or overlays) that can contain this element. */
+ contents: Set<ContentKey>,
+ identity: Any = Object(),
+ ) : this(debugName, MovableElementContentPicker(contents), identity)
+
+ override fun toString(): String {
+ return "MovableElementKey(debugName=$debugName)"
+ }
+}
+
/** Key for a shared value of an element. */
class ValueKey(debugName: String, identity: Any = Object()) : Key(debugName, identity) {
override fun toString(): String {
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 e556f6f4ff05..abecdd771f7a 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
@@ -28,6 +28,7 @@ import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.util.fastLastOrNull
import com.android.compose.animation.scene.content.Content
+import com.android.compose.animation.scene.content.state.TransitionState
@Composable
internal fun Element(
@@ -53,7 +54,7 @@ internal fun Element(
internal fun MovableElement(
layoutImpl: SceneTransitionLayoutImpl,
sceneOrOverlay: Content,
- key: ElementKey,
+ key: MovableElementKey,
modifier: Modifier,
content: @Composable ElementScope<MovableElementContentScope>.() -> Unit,
) {
@@ -111,7 +112,7 @@ private class ElementScopeImpl(
private class MovableElementScopeImpl(
private val layoutImpl: SceneTransitionLayoutImpl,
- private val element: ElementKey,
+ private val element: MovableElementKey,
private val content: Content,
private val baseContentScope: BaseContentScope,
private val boxScope: BoxScope,
@@ -169,7 +170,7 @@ private class MovableElementScopeImpl(
private fun shouldComposeMovableElement(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
- element: ElementKey,
+ element: MovableElementKey,
): Boolean {
val transitions = layoutImpl.state.currentTransitions
if (transitions.isEmpty()) {
@@ -181,14 +182,10 @@ private fun shouldComposeMovableElement(
// The current transition for this element is the last transition in which either fromScene or
// toScene contains the element.
+ val contents = element.contentPicker.contents
val transition =
transitions.fastLastOrNull { transition ->
- element.scenePicker.sceneDuringTransition(
- element = element,
- transition = transition,
- fromSceneZIndex = layoutImpl.scenes.getValue(transition.fromScene).zIndex,
- toSceneZIndex = layoutImpl.scenes.getValue(transition.toScene).zIndex,
- ) != null
+ transition.fromScene in contents || transition.toScene in contents
} ?: return false
// Always compose movable elements in the scene picked by their scene picker.
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 f1b2249fc29e..ae5344fd7922 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
@@ -17,6 +17,7 @@
package com.android.compose.animation.scene
import androidx.compose.runtime.snapshotFlow
+import com.android.compose.animation.scene.content.state.TransitionState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
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 fd6762b97f79..fe16ef75118b 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
@@ -25,6 +25,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
+import com.android.compose.animation.scene.content.state.TransitionState
import kotlin.coroutines.cancellation.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 3401af827d4c..65a73676d398 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -189,7 +189,7 @@ interface BaseContentScope : ElementStateScope {
*/
@Composable
fun MovableElement(
- key: ElementKey,
+ key: MovableElementKey,
modifier: Modifier,
// TODO(b/317026105): As discussed in http://shortn/_gJVdltF8Si, remove the @Composable
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 01ce2111b8f2..a6c6a80941fc 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
@@ -18,10 +18,6 @@ package com.android.compose.animation.scene
import android.util.Log
import androidx.annotation.VisibleForTesting
-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 androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -29,12 +25,12 @@ 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
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
/**
* The state of a [SceneTransitionLayout].
@@ -146,221 +142,6 @@ fun MutableSceneTransitionLayoutState(
)
}
-@Stable
-sealed interface TransitionState {
- /**
- * The current effective scene. If a new 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
- * B, then [currentScene] becomes scene B even if the transition is not finished yet and is
- * still animating to settle to scene B.
- */
- val currentScene: SceneKey
-
- /** No transition/animation is currently running. */
- data class Idle(override val currentScene: SceneKey) : TransitionState
-
- /** There is a transition animating between two scenes. */
- abstract class Transition(
- /** 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 transition that `this` transition is replacing, if any. */
- internal val replacedTransition: Transition? = null,
- ) : TransitionState {
- /**
- * 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
-
- /**
- * 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.
- */
- open val previewProgress: Float = 0f
-
- /** The current velocity of [previewProgress], in progress units. */
- open val previewProgressVelocity: Float = 0f
-
- /** Whether the transition is currently in the preview stage */
- open val isInPreviewStage: Boolean = false
-
- /** 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 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 bouncingScene = bouncingScene
- return when {
- progress < 0f || bouncingScene == fromScene -> fromOverscrollSpec
- progress > 1f || bouncingScene == toScene -> toOverscrollSpec
- else -> null
- }
- }
-
- /** 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()
- }
-
- /**
- * 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
-
- init {
- check(fromScene != toScene)
- check(
- replacedTransition == null ||
- (replacedTransition.fromScene == fromScene &&
- replacedTransition.toScene == toScene)
- )
- }
-
- /**
- * Force this transition to finish and animate to [currentScene], so that this transition
- * progress will settle to either 0% (if [currentScene] == [fromScene]) or 100% (if
- * [currentScene] == [toScene]) in a finite amount of time.
- *
- * @return the [Job] that animates the progress to [currentScene]. It can be used to wait
- * until the animation is complete or cancel it to snap to [currentScene]. Calling
- * [finish] multiple times will return the same [Job].
- */
- abstract fun finish(): Job
-
- /**
- * Whether we are transitioning. If [from] or [to] is empty, we will also check that they
- * match the scenes we are animating from and/or to.
- */
- fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean {
- return (from == null || fromScene == from) && (to == null || toScene == to)
- }
-
- /** Whether we are transitioning from [scene] to [other], or from [other] to [scene]. */
- fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean {
- return isTransitioning(from = scene, to = other) ||
- isTransitioning(from = other, to = scene)
- }
-
- internal fun updateOverscrollSpecs(
- fromSpec: OverscrollSpecImpl?,
- toSpec: OverscrollSpecImpl?,
- ) {
- fromOverscrollSpec = fromSpec
- toOverscrollSpec = toSpec
- }
-
- 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.toScene].
- *
- * Used to understand the direction of the overscroll.
- */
- val isUpOrLeft: Boolean
-
- /**
- * The relative orientation between [Transition.fromScene] and [Transition.toScene].
- *
- * 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.fromScene] and [Transition.toScene].
- */
- val overscrollScope: OverscrollScope
-
- /**
- * The scene around which the transition is currently bouncing. When not `null`, this
- * transition is currently oscillating around this scene and will soon settle to that scene.
- */
- val bouncingScene: SceneKey?
-
- companion object {
- const val DistanceUnspecified = 0f
- }
- }
-}
-
/** A [MutableSceneTransitionLayoutState] that holds the value for the current scene. */
internal class MutableSceneTransitionLayoutStateImpl(
initialScene: SceneKey,
@@ -459,7 +240,7 @@ internal class MutableSceneTransitionLayoutStateImpl(
// Compute the [TransformationSpec] when the transition starts.
val fromScene = transition.fromScene
val toScene = transition.toScene
- val orientation = (transition as? TransitionState.HasOverscrollProperties)?.orientation
+ val orientation = (transition as? ContentState.HasOverscrollProperties)?.orientation
// Update the transition specs.
transition.transformationSpec =
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index cfa4c70c8239..3ded1de0f3e1 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -157,16 +157,16 @@ interface TransitionSpec {
val key: TransitionKey?
/**
- * The scene we are transitioning from. If `null`, this spec can be used to animate from any
- * scene.
+ * The content we are transitioning from. If `null`, this spec can be used to animate from any
+ * content.
*/
- val from: SceneKey?
+ val from: ContentKey?
/**
- * The scene we are transitioning to. If `null`, this spec can be used to animate from any
- * scene.
+ * The content we are transitioning to. If `null`, this spec can be used to animate from any
+ * content.
*/
- val to: SceneKey?
+ val to: ContentKey?
/**
* Return a reversed version of this [TransitionSpec] for a transition going from [to] to
@@ -231,8 +231,8 @@ interface TransformationSpec {
internal class TransitionSpecImpl(
override val key: TransitionKey?,
- override val from: SceneKey?,
- override val to: SceneKey?,
+ override val from: ContentKey?,
+ override val to: ContentKey?,
private val previewTransformationSpec: (() -> TransformationSpecImpl)? = null,
private val reversePreviewTransformationSpec: (() -> TransformationSpecImpl)? = null,
private val transformationSpec: () -> TransformationSpecImpl
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 ecef0f49f206..38b83835ad29 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
@@ -23,7 +23,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.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
+import com.android.compose.animation.scene.content.state.ContentState
/** Define the [transitions][SceneTransitions] to be used with a [SceneTransitionLayout]. */
fun transitions(builder: SceneTransitionsBuilder.() -> Unit): SceneTransitions {
@@ -47,9 +47,9 @@ interface SceneTransitionsBuilder {
var interruptionHandler: InterruptionHandler
/**
- * Define the default animation to be played when transitioning [to] the specified scene, from
- * any scene. For the animation specification to apply only when transitioning between two
- * specific scenes, use [from] instead.
+ * Define the default animation to be played when transitioning [to] the specified content, from
+ * any content. For the animation specification to apply only when transitioning between two
+ * specific contents, use [from] instead.
*
* If [key] is not `null`, then this transition will only be used if the same key is specified
* when triggering the transition.
@@ -61,7 +61,7 @@ interface SceneTransitionsBuilder {
* @see from
*/
fun to(
- to: SceneKey,
+ to: ContentKey,
key: TransitionKey? = null,
preview: (TransitionBuilder.() -> Unit)? = null,
reversePreview: (TransitionBuilder.() -> Unit)? = null,
@@ -69,12 +69,12 @@ interface SceneTransitionsBuilder {
): TransitionSpec
/**
- * Define the animation to be played when transitioning [from] the specified scene. For the
- * animation specification to apply only when transitioning between two specific scenes, pass
- * the destination scene via the [to] argument.
+ * Define the animation to be played when transitioning [from] the specified content. For the
+ * animation specification to apply only when transitioning between two specific contents, pass
+ * the destination content via the [to] argument.
*
- * When looking up which transition should be used when animating from scene A to scene B, we
- * pick the single transition with the given [key] and matching one of these predicates (in
+ * When looking up which transition should be used when animating from content A to content B,
+ * we pick the single transition with the given [key] and matching one of these predicates (in
* order of importance):
* 1. from == A && to == B
* 2. to == A && from == B, which is then treated in reverse.
@@ -86,8 +86,8 @@ interface SceneTransitionsBuilder {
* reversible with the reverse animation having a preview as well, define a [reversePreview].
*/
fun from(
- from: SceneKey,
- to: SceneKey? = null,
+ from: ContentKey,
+ to: ContentKey? = null,
key: TransitionKey? = null,
preview: (TransitionBuilder.() -> Unit)? = null,
reversePreview: (TransitionBuilder.() -> Unit)? = null,
@@ -233,99 +233,171 @@ interface OverscrollScope : Density {
/**
* An interface to decide where we should draw shared Elements or compose MovableElements.
*
- * @see DefaultElementScenePicker
- * @see HighestZIndexScenePicker
- * @see LowestZIndexScenePicker
- * @see MovableElementScenePicker
+ * @see DefaultElementContentPicker
+ * @see HighestZIndexContentPicker
+ * @see LowestZIndexContentPicker
+ * @see MovableElementContentPicker
*/
-interface ElementScenePicker {
+interface ElementContentPicker {
/**
- * Return the scene in which [element] should be drawn (when using `Modifier.element(key)`) or
+ * Return the content in which [element] should be drawn (when using `Modifier.element(key)`) or
* composed (when using `MovableElement(key)`) during the given [transition]. If this element
- * should not be drawn or composed in neither [transition.fromScene] nor [transition.toScene],
- * return `null`.
+ * should not be drawn or composed in neither [transition.fromContent] nor
+ * [transition.toContent], return `null`.
*
- * Important: For [MovableElements][ContentScope.MovableElement], this scene picker will
+ * Important: For [MovableElements][ContentScope.MovableElement], this content picker will
* *always* be used during transitions to decide whether we should compose that element in a
- * given scene or not. Therefore, you should make sure that the returned [SceneKey] contains the
- * movable element, otherwise that element will not be composed in any scene during the
+ * given content or not. Therefore, you should make sure that the returned [ContentKey] contains
+ * the movable element, otherwise that element will not be composed in any scene during the
* transition.
*/
- fun sceneDuringTransition(
+ fun contentDuringTransition(
element: ElementKey,
- transition: TransitionState.Transition,
- fromSceneZIndex: Float,
- toSceneZIndex: Float,
- ): SceneKey?
+ transition: ContentState.Transition<*>,
+ fromContentZIndex: Float,
+ toContentZIndex: Float,
+ ): ContentKey
/**
- * Return [transition.fromScene] if it is in [scenes] and [transition.toScene] is not, or return
- * [transition.toScene] if it is in [scenes] and [transition.fromScene] is not. If neither
- * [transition.fromScene] and [transition.toScene] are in [scenes], return `null`. If both
- * [transition.fromScene] and [transition.toScene] are in [scenes], throw an exception.
+ * Return [transition.fromContent] if it is in [contents] and [transition.toContent] is not, or
+ * return [transition.toContent] if it is in [contents] and [transition.fromContent] is not. If
+ * neither [transition.toContent] and [transition.fromContent] are in [contents] or if both
+ * [transition.fromContent] and [transition.toContent] are in [contents], throw an exception.
*
- * This function can be useful when computing the scene in which a movable element should be
+ * This function can be useful when computing the content in which a movable element should be
* composed.
*/
- fun pickSingleSceneIn(
- scenes: Set<SceneKey>,
- transition: TransitionState.Transition,
+ fun pickSingleContentIn(
+ contents: Set<ContentKey>,
+ transition: ContentState.Transition<*>,
element: ElementKey,
- ): SceneKey? {
- val fromScene = transition.fromScene
- val toScene = transition.toScene
- val fromSceneInScenes = scenes.contains(fromScene)
- val toSceneInScenes = scenes.contains(toScene)
+ ): ContentKey {
+ val fromContent = transition.fromContent
+ val toContent = transition.toContent
+ val fromContentInContents = contents.contains(fromContent)
+ val toContentInContents = contents.contains(toContent)
+
+ if (fromContentInContents && toContentInContents) {
+ error(
+ "Element $element can be in both $fromContent and $toContent. You should add a " +
+ "special case for this transition before calling pickSingleSceneIn()."
+ )
+ }
- return when {
- fromSceneInScenes && toSceneInScenes -> {
- error(
- "Element $element can be in both $fromScene and $toScene. You should add a " +
- "special case for this transition before calling pickSingleSceneIn()."
- )
- }
- fromSceneInScenes -> fromScene
- toSceneInScenes -> toScene
- else -> null
+ if (!fromContentInContents && !toContentInContents) {
+ error(
+ "Element $element can be neither in $fromContent and $toContent. This either " +
+ "means that you should add one of them in the scenes set passed to " +
+ "pickSingleSceneIn(), or there is an internal error and this element was " +
+ "composed when it shouldn't be."
+ )
+ }
+
+ return if (fromContentInContents) {
+ fromContent
+ } else {
+ toContent
}
}
}
-/** An [ElementScenePicker] that draws/composes elements in the scene with the highest z-order. */
-object HighestZIndexScenePicker : ElementScenePicker {
- override fun sceneDuringTransition(
+/**
+ * An element picker on which we can query the set of contents (scenes or overlays) that contain the
+ * element. This is needed by [MovableElement], that needs to know at composition time on which of
+ * the candidate contents an element should be composed.
+ */
+interface StaticElementContentPicker : ElementContentPicker {
+ /** The exhaustive lists of contents that contain this element. */
+ val contents: Set<ContentKey>
+}
+
+/**
+ * An [ElementContentPicker] that draws/composes elements in the content with the highest z-order.
+ */
+object HighestZIndexContentPicker : ElementContentPicker {
+ override fun contentDuringTransition(
element: ElementKey,
- transition: TransitionState.Transition,
- fromSceneZIndex: Float,
- toSceneZIndex: Float
- ): SceneKey {
- return if (fromSceneZIndex > toSceneZIndex) {
- transition.fromScene
+ transition: ContentState.Transition<*>,
+ fromContentZIndex: Float,
+ toContentZIndex: Float
+ ): ContentKey {
+ return if (fromContentZIndex > toContentZIndex) {
+ transition.fromContent
} else {
- transition.toScene
+ transition.toContent
+ }
+ }
+
+ /**
+ * Return a [StaticElementContentPicker] that behaves like [HighestZIndexContentPicker] and can
+ * be used by [MovableElement].
+ */
+ operator fun invoke(contents: Set<ContentKey>): StaticElementContentPicker {
+ return object : StaticElementContentPicker {
+ override val contents: Set<ContentKey> = contents
+
+ override fun contentDuringTransition(
+ element: ElementKey,
+ transition: ContentState.Transition<*>,
+ fromContentZIndex: Float,
+ toContentZIndex: Float
+ ): ContentKey {
+ return HighestZIndexContentPicker.contentDuringTransition(
+ element,
+ transition,
+ fromContentZIndex,
+ toContentZIndex,
+ )
+ }
}
}
}
-/** An [ElementScenePicker] that draws/composes elements in the scene with the lowest z-order. */
-object LowestZIndexScenePicker : ElementScenePicker {
- override fun sceneDuringTransition(
+/**
+ * An [ElementContentPicker] that draws/composes elements in the content with the lowest z-order.
+ */
+object LowestZIndexContentPicker : ElementContentPicker {
+ override fun contentDuringTransition(
element: ElementKey,
- transition: TransitionState.Transition,
- fromSceneZIndex: Float,
- toSceneZIndex: Float
- ): SceneKey {
- return if (fromSceneZIndex < toSceneZIndex) {
- transition.fromScene
+ transition: ContentState.Transition<*>,
+ fromContentZIndex: Float,
+ toContentZIndex: Float
+ ): ContentKey {
+ return if (fromContentZIndex < toContentZIndex) {
+ transition.fromContent
} else {
- transition.toScene
+ transition.toContent
+ }
+ }
+
+ /**
+ * Return a [StaticElementContentPicker] that behaves like [LowestZIndexContentPicker] and can
+ * be used by [MovableElement].
+ */
+ operator fun invoke(contents: Set<ContentKey>): StaticElementContentPicker {
+ return object : StaticElementContentPicker {
+ override val contents: Set<ContentKey> = contents
+
+ override fun contentDuringTransition(
+ element: ElementKey,
+ transition: ContentState.Transition<*>,
+ fromContentZIndex: Float,
+ toContentZIndex: Float
+ ): ContentKey {
+ return LowestZIndexContentPicker.contentDuringTransition(
+ element,
+ transition,
+ fromContentZIndex,
+ toContentZIndex,
+ )
+ }
}
}
}
/**
- * An [ElementScenePicker] that draws/composes elements in the scene we are transitioning to, iff
- * that scene is in [scenes].
+ * An [ElementContentPicker] that draws/composes elements in the content we are transitioning to,
+ * iff that content is in [contents].
*
* This picker can be useful for movable elements whose content size depends on its content (because
* it wraps it) in at least one scene. That way, the target size of the MovableElement will be
@@ -337,23 +409,30 @@ object LowestZIndexScenePicker : ElementScenePicker {
* is not the same as when going from scene B to scene A, so it's not usable in situations where
* z-ordering during the transition matters.
*/
-class MovableElementScenePicker(private val scenes: Set<SceneKey>) : ElementScenePicker {
- override fun sceneDuringTransition(
+class MovableElementContentPicker(
+ override val contents: Set<ContentKey>,
+) : StaticElementContentPicker {
+ override fun contentDuringTransition(
element: ElementKey,
- transition: TransitionState.Transition,
- fromSceneZIndex: Float,
- toSceneZIndex: Float,
- ): SceneKey? {
+ transition: ContentState.Transition<*>,
+ fromContentZIndex: Float,
+ toContentZIndex: Float,
+ ): ContentKey {
return when {
- scenes.contains(transition.toScene) -> transition.toScene
- scenes.contains(transition.fromScene) -> transition.fromScene
- else -> null
+ transition.toContent in contents -> transition.toContent
+ else -> {
+ check(transition.fromContent in contents) {
+ "Neither ${transition.fromContent} nor ${transition.toContent} are in " +
+ "contents. This transition should not have been used for this element."
+ }
+ transition.fromContent
+ }
}
}
}
-/** The default [ElementScenePicker]. */
-val DefaultElementScenePicker = HighestZIndexScenePicker
+/** The default [ElementContentPicker]. */
+val DefaultElementContentPicker = HighestZIndexContentPicker
@TransitionDsl
interface PropertyTransformationBuilder {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index a0759c73f76e..6515cb8f68ca 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -60,7 +60,7 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder {
val transitionOverscrollSpecs = mutableListOf<OverscrollSpecImpl>()
override fun to(
- to: SceneKey,
+ to: ContentKey,
key: TransitionKey?,
preview: (TransitionBuilder.() -> Unit)?,
reversePreview: (TransitionBuilder.() -> Unit)?,
@@ -70,8 +70,8 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder {
}
override fun from(
- from: SceneKey,
- to: SceneKey?,
+ from: ContentKey,
+ to: ContentKey?,
key: TransitionKey?,
preview: (TransitionBuilder.() -> Unit)?,
reversePreview: (TransitionBuilder.() -> Unit)?,
@@ -120,8 +120,8 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder {
}
private fun transition(
- from: SceneKey?,
- to: SceneKey?,
+ from: ContentKey?,
+ to: ContentKey?,
key: TransitionKey?,
preview: (TransitionBuilder.() -> Unit)?,
reversePreview: (TransitionBuilder.() -> Unit)?,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
index 492d2115cfdc..6f608cbc1d7f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
@@ -39,6 +39,7 @@ import com.android.compose.animation.scene.ElementScope
import com.android.compose.animation.scene.ElementStateScope
import com.android.compose.animation.scene.MovableElement
import com.android.compose.animation.scene.MovableElementContentScope
+import com.android.compose.animation.scene.MovableElementKey
import com.android.compose.animation.scene.NestedScrollBehavior
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.SceneTransitionLayoutState
@@ -130,7 +131,7 @@ internal class ContentScopeImpl(
@Composable
override fun MovableElement(
- key: ElementKey,
+ key: MovableElementKey,
modifier: Modifier,
content: @Composable (ElementScope<MovableElementContentScope>.() -> Unit)
) {
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
new file mode 100644
index 000000000000..add393441794
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt
@@ -0,0 +1,237 @@
+/*
+ * 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 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
+
+ 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].
+ */
+ 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
new file mode 100644
index 000000000000..77de22ceeec4
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.runtime.Stable
+import com.android.compose.animation.scene.SceneKey
+
+/** The state associated to one or more scenes. */
+// TODO(b/353679003): Rename to SceneState.
+@Stable
+sealed interface TransitionState : ContentState<SceneKey> {
+ /**
+ * The current effective scene. If a new 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
+ * B, then [currentScene] becomes scene B even if the transition is not finished yet and is
+ * still animating to settle to scene B.
+ */
+ val currentScene: SceneKey
+
+ /** The scene [currentScene] is idle. */
+ data class Idle(
+ override val currentScene: SceneKey,
+ ) : TransitionState, ContentState.Idle<SceneKey>(currentScene)
+
+ /** 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,
+
+ /** 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)
+}
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 65d4d2da4dd1..538ce799c3eb 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
@@ -21,9 +21,8 @@ import com.android.compose.animation.scene.ContentKey
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.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
/** Anchor the size of an element to the size of another element. */
internal class AnchoredSize(
@@ -36,19 +35,19 @@ internal class AnchoredSize(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: Element,
- sceneState: Element.State,
- transition: TransitionState.Transition,
+ stateInContent: Element.State,
+ transition: ContentState.Transition<*>,
value: IntSize,
): IntSize {
- fun anchorSizeIn(scene: SceneKey): IntSize {
+ fun anchorSizeIn(content: ContentKey): IntSize {
val size =
- layoutImpl.elements[anchor]?.stateByContent?.get(scene)?.targetSize?.takeIf {
+ layoutImpl.elements[anchor]?.stateByContent?.get(content)?.targetSize?.takeIf {
it != Element.SizeUnspecified
}
?: throwMissingAnchorException(
transformation = "AnchoredSize",
anchor = anchor,
- scene = scene,
+ content = content,
)
return IntSize(
@@ -60,10 +59,10 @@ internal class AnchoredSize(
// This simple implementation assumes that the size of [element] is the same as the size of
// the [anchor] in [scene], so simply transform to the size of the anchor in the other
// scene.
- return if (content == transition.fromScene) {
- anchorSizeIn(transition.toScene)
+ return if (content == transition.fromContent) {
+ anchorSizeIn(transition.toContent)
} else {
- anchorSizeIn(transition.fromScene)
+ anchorSizeIn(transition.fromContent)
}
}
}
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 8d7e1c971acf..258f54122711 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
@@ -22,9 +22,8 @@ import com.android.compose.animation.scene.ContentKey
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.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
/** Anchor the translation of an element to another element. */
internal class AnchoredTranslate(
@@ -35,33 +34,33 @@ internal class AnchoredTranslate(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: Element,
- sceneState: Element.State,
- transition: TransitionState.Transition,
+ stateInContent: Element.State,
+ transition: ContentState.Transition<*>,
value: Offset,
): Offset {
- fun throwException(scene: SceneKey?): Nothing {
+ fun throwException(content: ContentKey?): Nothing {
throwMissingAnchorException(
transformation = "AnchoredTranslate",
anchor = anchor,
- scene = scene,
+ content = content,
)
}
- val anchor = layoutImpl.elements[anchor] ?: throwException(scene = null)
- fun anchorOffsetIn(scene: SceneKey): Offset? {
- return anchor.stateByContent[scene]?.targetOffset?.takeIf { it.isSpecified }
+ val anchor = layoutImpl.elements[anchor] ?: throwException(content = null)
+ fun anchorOffsetIn(content: ContentKey): Offset? {
+ return anchor.stateByContent[content]?.targetOffset?.takeIf { it.isSpecified }
}
// [element] will move the same amount as [anchor] does.
// TODO(b/290184746): Also support anchors that are not shared but translated because of
// other transformations, like an edge translation.
val anchorFromOffset =
- anchorOffsetIn(transition.fromScene) ?: throwException(transition.fromScene)
+ anchorOffsetIn(transition.fromContent) ?: throwException(transition.fromContent)
val anchorToOffset =
- anchorOffsetIn(transition.toScene) ?: throwException(transition.toScene)
+ anchorOffsetIn(transition.toContent) ?: throwException(transition.toContent)
val offset = anchorToOffset - anchorFromOffset
- return if (content == transition.toScene) {
+ return if (content == transition.toContent) {
Offset(
value.x - offset.x,
value.y - offset.y,
@@ -78,11 +77,11 @@ internal class AnchoredTranslate(
internal fun throwMissingAnchorException(
transformation: String,
anchor: ElementKey,
- scene: SceneKey?,
+ content: ContentKey?,
): Nothing {
error(
"""
- Anchor ${anchor.debugName} does not have a target state in scene ${scene?.debugName}.
+ Anchor ${anchor.debugName} does not have a target state in content ${content?.debugName}.
This either means that it was not composed at all during the transition or that it was
composed too late, for instance during layout/subcomposition. To avoid flickers in
$transformation, you should make sure that the composition and layout of anchor is *not*
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 f010c3b1d3b4..be8dac21a5cf 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.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
/**
* Scales the draw size of an element. Note this will only scale the draw inside of an element,
@@ -39,8 +39,8 @@ internal class DrawScale(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: Element,
- sceneState: Element.State,
- transition: TransitionState.Transition,
+ stateInContent: Element.State,
+ transition: ContentState.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 dfce997ba190..d72e43abe8f4 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.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
/** Translate an element from an edge of the layout. */
internal class EdgeTranslate(
@@ -34,12 +34,12 @@ internal class EdgeTranslate(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: Element,
- sceneState: Element.State,
- transition: TransitionState.Transition,
+ stateInContent: Element.State,
+ transition: ContentState.Transition<*>,
value: Offset
): Offset {
val sceneSize = layoutImpl.content(content).targetSize
- val elementSize = sceneState.targetSize
+ val elementSize = stateInContent.targetSize
if (elementSize == Element.SizeUnspecified) {
return value
}
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 c1bb017143a9..92ae30f8e3db 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.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
/** Fade an element in or out. */
internal class Fade(
@@ -30,8 +30,8 @@ internal class Fade(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: Element,
- sceneState: Element.State,
- transition: TransitionState.Transition,
+ stateInContent: Element.State,
+ transition: ContentState.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 5adbf7eb2614..e8515dce5fec 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.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
import kotlin.math.roundToInt
/**
@@ -37,8 +37,8 @@ internal class ScaleSize(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: Element,
- sceneState: Element.State,
- transition: TransitionState.Transition,
+ stateInContent: Element.State,
+ transition: ContentState.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 24b71944b6d0..77ec89161d43 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
@@ -23,7 +23,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.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
/** A transformation applied to one or more elements during a transition. */
sealed interface Transformation {
@@ -63,8 +63,8 @@ internal sealed interface PropertyTransformation<T> : Transformation {
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: Element,
- sceneState: Element.State,
- transition: TransitionState.Transition,
+ stateInContent: Element.State,
+ transition: ContentState.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 123756ae4211..fab4ced1ff44 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.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
internal class Translate(
override val matcher: ElementMatcher,
@@ -35,8 +35,8 @@ internal class Translate(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: Element,
- sceneState: Element.State,
- transition: TransitionState.Transition,
+ stateInContent: Element.State,
+ transition: ContentState.Transition<*>,
value: Offset,
): Offset {
return with(layoutImpl.density) {
@@ -57,14 +57,14 @@ internal class OverscrollTranslate(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: Element,
- sceneState: Element.State,
- transition: TransitionState.Transition,
+ stateInContent: Element.State,
+ transition: ContentState.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 TransitionState.HasOverscrollProperties
+ val overscrollProperties = transition as ContentState.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 ed9888560f05..23bcf109eb98 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
@@ -18,7 +18,7 @@ package com.android.compose.animation.scene.transition.link
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.TransitionState
import kotlinx.coroutines.Job
/** A linked transition which is driven by a [originalTransition]. */
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 2018d6e37b57..c0c40dd23897 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
@@ -20,7 +20,7 @@ import com.android.compose.animation.scene.MutableSceneTransitionLayoutStateImpl
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayoutState
import com.android.compose.animation.scene.TransitionKey
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.TransitionState
/** A link between a source (implicit) and [target] `SceneTransitionLayoutState`. */
class StateLink(target: SceneTransitionLayoutState, val transitionLinks: List<TransitionLink>) {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
index 87162987bb00..01895c91a399 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
@@ -88,11 +88,11 @@ class AnimatedSharedAsStateTest {
@Composable
private fun ContentScope.MovableFoo(
+ key: MovableElementKey,
targetValues: Values,
onCurrentValueChanged: (Values) -> Unit,
) {
- val key = TestElements.Foo
- MovableElement(key = key, Modifier) {
+ MovableElement(key, Modifier) {
val int by animateElementIntAsState(targetValues.int, key = TestValues.Value1)
val float by animateElementFloatAsState(targetValues.float, key = TestValues.Value2)
val dp by animateElementDpAsState(targetValues.dp, key = TestValues.Value3)
@@ -183,15 +183,22 @@ class AnimatedSharedAsStateTest {
var lastValueInFrom = fromValues
var lastValueInTo = toValues
+ val key = MovableElementKey("Foo", contents = setOf(SceneA, SceneB))
+
rule.testTransition(
fromSceneContent = {
MovableFoo(
+ key = key,
targetValues = fromValues,
- onCurrentValueChanged = { lastValueInFrom = it }
+ onCurrentValueChanged = { lastValueInFrom = it },
)
},
toSceneContent = {
- MovableFoo(targetValues = toValues, onCurrentValueChanged = { lastValueInTo = it })
+ MovableFoo(
+ key = key,
+ targetValues = toValues,
+ onCurrentValueChanged = { lastValueInTo = it },
+ )
},
transition = {
// The transition lasts 64ms = 4 frames.
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 be89b18a02c3..dc5b2f78180f 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
@@ -35,7 +35,8 @@ import com.android.compose.animation.scene.NestedScrollBehavior.EdgeWithPreview
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
-import com.android.compose.animation.scene.TransitionState.Transition
+import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.compose.animation.scene.content.state.TransitionState.Transition
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.test.MonotonicClockTestScope
import com.android.compose.test.runMonotonicClockTest
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementScenePickerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementContentPickerTest.kt
index 3b022e8adc72..96e521bced29 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementScenePickerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementContentPickerTest.kt
@@ -31,12 +31,12 @@ import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
-class ElementScenePickerTest {
+class ElementContentPickerTest {
@get:Rule val rule = createComposeRule()
@Test
fun highestZIndexPicker() {
- val key = ElementKey("TestElement", scenePicker = HighestZIndexScenePicker)
+ val key = ElementKey("TestElement", contentPicker = HighestZIndexContentPicker)
rule.testTransition(
fromSceneContent = { Box(Modifier.element(key).size(10.dp)) },
toSceneContent = { Box(Modifier.element(key).size(10.dp)) },
@@ -62,7 +62,7 @@ class ElementScenePickerTest {
@Test
fun lowestZIndexPicker() {
- val key = ElementKey("TestElement", scenePicker = LowestZIndexScenePicker)
+ val key = ElementKey("TestElement", contentPicker = LowestZIndexContentPicker)
rule.testTransition(
fromSceneContent = { Box(Modifier.element(key).size(10.dp)) },
toSceneContent = { Box(Modifier.element(key).size(10.dp)) },
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 63df7a93375c..75f44ff9cfe0 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
@@ -1723,9 +1723,11 @@ class ElementTest {
val fooInA = "fooInA"
val fooInB = "fooInB"
+ val key = MovableElementKey("Foo", contents = setOf(SceneA, SceneB))
+
@Composable
fun ContentScope.MovableFoo(text: String, modifier: Modifier = Modifier) {
- MovableElement(TestElements.Foo, modifier) { content { Text(text) } }
+ MovableElement(key, modifier) { content { Text(text) } }
}
rule.setContent {
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 3552d3d0bc1b..ca72181e4fcf 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
@@ -22,6 +22,7 @@ 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.TestScenes.SceneC
+import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.test.runMonotonicClockTest
import com.google.common.truth.Correspondence
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt
index 6745fbe064ff..e1d09453ee64 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt
@@ -18,20 +18,22 @@ package com.android.compose.animation.scene
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
-class MovableElementScenePickerTest {
+class MovableElementContentPickerTest {
@Test
fun toSceneInScenes() {
- val picker = MovableElementScenePicker(scenes = setOf(TestScenes.SceneA, TestScenes.SceneB))
+ val picker =
+ MovableElementContentPicker(contents = setOf(TestScenes.SceneA, TestScenes.SceneB))
assertThat(
- picker.sceneDuringTransition(
+ picker.contentDuringTransition(
TestElements.Foo,
transition(from = TestScenes.SceneA, to = TestScenes.SceneB),
- fromSceneZIndex = 0f,
- toSceneZIndex = 1f,
+ fromContentZIndex = 0f,
+ toContentZIndex = 1f,
)
)
.isEqualTo(TestScenes.SceneB)
@@ -39,13 +41,13 @@ class MovableElementScenePickerTest {
@Test
fun fromSceneInScenes() {
- val picker = MovableElementScenePicker(scenes = setOf(TestScenes.SceneA))
+ val picker = MovableElementContentPicker(contents = setOf(TestScenes.SceneA))
assertThat(
- picker.sceneDuringTransition(
+ picker.contentDuringTransition(
TestElements.Foo,
transition(from = TestScenes.SceneA, to = TestScenes.SceneB),
- fromSceneZIndex = 0f,
- toSceneZIndex = 1f,
+ fromContentZIndex = 0f,
+ toContentZIndex = 1f,
)
)
.isEqualTo(TestScenes.SceneA)
@@ -53,15 +55,14 @@ class MovableElementScenePickerTest {
@Test
fun noneInScenes() {
- val picker = MovableElementScenePicker(scenes = emptySet())
- assertThat(
- picker.sceneDuringTransition(
- TestElements.Foo,
- transition(from = TestScenes.SceneA, to = TestScenes.SceneB),
- fromSceneZIndex = 0f,
- toSceneZIndex = 1f,
- )
+ val picker = MovableElementContentPicker(contents = emptySet())
+ assertThrows(IllegalStateException::class.java) {
+ picker.contentDuringTransition(
+ TestElements.Foo,
+ transition(from = TestScenes.SceneA, to = TestScenes.SceneB),
+ fromContentZIndex = 0f,
+ toContentZIndex = 1f,
)
- .isEqualTo(null)
+ }
}
}
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 821cc2927443..520e7599f56a 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
@@ -43,6 +43,10 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
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
import com.google.common.truth.Truth.assertThat
@@ -62,7 +66,7 @@ class MovableElementTest {
}
@Composable
- private fun ContentScope.MovableCounter(key: ElementKey, modifier: Modifier) {
+ private fun ContentScope.MovableCounter(key: MovableElementKey, modifier: Modifier) {
MovableElement(key, modifier) { content { Counter() } }
}
@@ -74,8 +78,8 @@ class MovableElementTest {
},
toSceneContent = { Box(Modifier.element(TestElements.Foo).size(100.dp)) { Counter() } },
transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) },
- fromScene = TestScenes.SceneA,
- toScene = TestScenes.SceneB,
+ fromScene = SceneA,
+ toScene = SceneB,
) {
before {
// Click 3 times on the counter.
@@ -103,7 +107,7 @@ class MovableElementTest {
rule
.onNode(
hasText("count: 3") and
- hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneA))
+ hasParent(isElement(TestElements.Foo, scene = SceneA))
)
.assertExists()
.assertIsNotDisplayed()
@@ -111,7 +115,7 @@ class MovableElementTest {
rule
.onNode(
hasText("count: 0") and
- hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneB))
+ hasParent(isElement(TestElements.Foo, scene = SceneB))
)
.assertIsDisplayed()
.assertSizeIsEqualTo(75.dp, 75.dp)
@@ -148,27 +152,30 @@ class MovableElementTest {
@Test
fun movableElementIsMovedAndComposedOnlyOnce() {
val key =
- ElementKey(
+ MovableElementKey(
"Foo",
- scenePicker =
- object : ElementScenePicker {
- override fun sceneDuringTransition(
+ contentPicker =
+ object : StaticElementContentPicker {
+ override val contents: Set<ContentKey> = setOf(SceneA, SceneB)
+
+ override fun contentDuringTransition(
element: ElementKey,
- transition: TransitionState.Transition,
- fromSceneZIndex: Float,
- toSceneZIndex: Float
- ): SceneKey {
- assertThat(transition).hasFromScene(TestScenes.SceneA)
- assertThat(transition).hasToScene(TestScenes.SceneB)
- assertThat(fromSceneZIndex).isEqualTo(0)
- assertThat(toSceneZIndex).isEqualTo(1)
+ transition: ContentState.Transition<*>,
+ fromContentZIndex: Float,
+ toContentZIndex: Float
+ ): ContentKey {
+ transition as TransitionState.Transition
+ assertThat(transition).hasFromScene(SceneA)
+ assertThat(transition).hasToScene(SceneB)
+ assertThat(fromContentZIndex).isEqualTo(0)
+ assertThat(toContentZIndex).isEqualTo(1)
// Compose Foo in Scene A if progress < 0.65f, otherwise compose it
// in Scene B.
return if (transition.progress < 0.65f) {
- TestScenes.SceneA
+ SceneA
} else {
- TestScenes.SceneB
+ SceneB
}
}
}
@@ -178,8 +185,8 @@ class MovableElementTest {
fromSceneContent = { MovableCounter(key, Modifier.size(50.dp)) },
toSceneContent = { MovableCounter(key, Modifier.size(100.dp)) },
transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) },
- fromScene = TestScenes.SceneA,
- toScene = TestScenes.SceneB,
+ fromScene = SceneA,
+ toScene = SceneB,
) {
before {
// Click 3 times on the counter.
@@ -207,7 +214,7 @@ class MovableElementTest {
rule
.onNode(
hasText("count: 3") and
- hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneA))
+ hasParent(isElement(TestElements.Foo, scene = SceneA))
)
.assertIsDisplayed()
.assertSizeIsEqualTo(75.dp, 75.dp)
@@ -228,7 +235,7 @@ class MovableElementTest {
rule
.onNode(
hasText("count: 3") and
- hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneB))
+ hasParent(isElement(TestElements.Foo, scene = SceneB))
)
.assertIsDisplayed()
@@ -263,17 +270,19 @@ class MovableElementTest {
@Test
fun movableElementContentIsRecomposedIfContentParametersChange() {
+ val key = MovableElementKey("Foo", contents = setOf(SceneA, SceneB))
+
@Composable
fun ContentScope.MovableFoo(text: String, modifier: Modifier = Modifier) {
- MovableElement(TestElements.Foo, modifier) { content { Text(text) } }
+ MovableElement(key, modifier) { content { Text(text) } }
}
rule.testTransition(
fromSceneContent = { MovableFoo(text = "fromScene") },
toSceneContent = { MovableFoo(text = "toScene") },
transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) },
- fromScene = TestScenes.SceneA,
- toScene = TestScenes.SceneB,
+ fromScene = SceneA,
+ toScene = SceneB,
) {
// Before the transition, only fromScene is composed.
before {
@@ -314,9 +323,10 @@ class MovableElementTest {
@Test
fun movableElementScopeExtendsBoxScope() {
+ val key = MovableElementKey("Foo", contents = setOf(SceneA))
rule.setContent {
TestContentScope {
- MovableElement(TestElements.Foo, Modifier.size(200.dp)) {
+ MovableElement(key, Modifier.size(200.dp)) {
content {
Box(Modifier.testTag("bottomEnd").align(Alignment.BottomEnd))
Box(Modifier.testTag("matchParentSize").matchParentSize())
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 52cceecadbf9..6b417ee5a468 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
@@ -25,6 +25,7 @@ import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.TestScenes.SceneD
+import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.animation.scene.transition.link.StateLink
import com.android.compose.test.runMonotonicClockTest
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 66d4059adfda..91bd7e1800cd 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,6 +17,8 @@
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
import kotlinx.coroutines.sync.Mutex
@@ -37,14 +39,14 @@ fun transition(
isInitiatedByUserInput: Boolean = false,
isUserInputOngoing: Boolean = false,
isUpOrLeft: Boolean = false,
- bouncingScene: SceneKey? = null,
+ bouncingContent: ContentKey? = null,
orientation: Orientation = Orientation.Horizontal,
onFinish: ((TransitionState.Transition) -> Job)? = null,
replacedTransition: TransitionState.Transition? = null,
): TransitionState.Transition {
return object :
TransitionState.Transition(from, to, replacedTransition),
- TransitionState.HasOverscrollProperties {
+ ContentState.HasOverscrollProperties {
override val currentScene: SceneKey
get() = current()
@@ -66,7 +68,7 @@ fun transition(
override val isInitiatedByUserInput: Boolean = isInitiatedByUserInput
override val isUserInputOngoing: Boolean = isUserInputOngoing
override val isUpOrLeft: Boolean = isUpOrLeft
- override val bouncingScene: SceneKey? = bouncingScene
+ override val bouncingContent: ContentKey? = bouncingContent
override val orientation: Orientation = orientation
override val overscrollScope: OverscrollScope =
object : OverscrollScope {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
index f4e0073843ce..68240b5337fe 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
@@ -59,7 +59,7 @@ class TransitionDslTest {
assertThat(transitions.transitionSpecs)
.comparingElementsUsing(
- Correspondence.transforming<TransitionSpecImpl, Pair<SceneKey?, SceneKey?>>(
+ Correspondence.transforming<TransitionSpecImpl, Pair<ContentKey?, ContentKey?>>(
{ it?.from to it?.to },
"has (from, to) equal to"
)
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 e997a75a7a38..a12ab7888a15 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
@@ -18,7 +18,8 @@ package com.android.compose.animation.scene.subjects
import com.android.compose.animation.scene.OverscrollSpec
import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.TransitionState
+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
import com.google.common.truth.Subject
@@ -132,12 +133,12 @@ private constructor(
}
fun hasBouncingScene(scene: SceneKey) {
- if (actual !is TransitionState.HasOverscrollProperties) {
- failWithActual(simpleFact("expected to be TransitionState.HasOverscrollProperties"))
+ if (actual !is ContentState.HasOverscrollProperties) {
+ failWithActual(simpleFact("expected to be ContentState.HasOverscrollProperties"))
}
- check("bouncingScene")
- .that((actual as TransitionState.HasOverscrollProperties).bouncingScene)
+ check("bouncingContent")
+ .that((actual as ContentState.HasOverscrollProperties).bouncingContent)
.isEqualTo(scene)
}