Add NotificationShade and QuickSettingsShade to the STL demo
This CL adds overlays to the STL demo, disabled by default by a config
flag.
See b/353679003#comment35 for videos.
Bug: 353679003
Test: Manual in the demo app
Flag: com.android.systemui.scene_container
Change-Id: Ic88b448689160d37924d62a21fe6472ee132aed3
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/DemoConfiguration.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/DemoConfiguration.kt
index aaa9cb6..dbc1ecb 100644
--- a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/DemoConfiguration.kt
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/DemoConfiguration.kt
@@ -67,13 +67,14 @@
val interactiveNotifications: Boolean = false,
val showMediaPlayer: Boolean = true,
val isFullscreen: Boolean = false,
- val canChangeScene: Boolean = true,
+ val canChangeSceneOrOverlays: Boolean = true,
val transitionInterceptionThreshold: Float = 0.05f,
val springConfigurations: DemoSpringConfigurations = DemoSpringConfigurations.presets[1],
val useOverscrollSpec: Boolean = true,
val overscrollProgressConverter: DemoOverscrollProgress = Tanh(maxProgress = 0.2f, tilt = 3f),
val enableInterruptions: Boolean = true,
val lsToShadeRequiresFullSwipe: ToggleableState = ToggleableState.Indeterminate,
+ val enableOverlays: Boolean = false,
) {
companion object {
val Saver = run {
@@ -82,7 +83,7 @@
val interactiveNotificationsKey = "interactiveNotifications"
val showMediaPlayerKey = "showMediaPlayer"
val isFullscreenKey = "isFullscreen"
- val canChangeSceneKey = "canChangeScene"
+ val canChangeSceneOrOverlaysKey = "canChangeSceneOrOverlays"
val transitionInterceptionThresholdKey = "transitionInterceptionThreshold"
val springConfigurationsKey = "springConfigurations"
val useOverscrollSpec = "useOverscrollSpec"
@@ -98,7 +99,7 @@
interactiveNotificationsKey to it.interactiveNotifications,
showMediaPlayerKey to it.showMediaPlayer,
isFullscreenKey to it.isFullscreen,
- canChangeSceneKey to it.canChangeScene,
+ canChangeSceneOrOverlaysKey to it.canChangeSceneOrOverlays,
transitionInterceptionThresholdKey to it.transitionInterceptionThreshold,
springConfigurationsKey to it.springConfigurations.save(),
useOverscrollSpec to it.useOverscrollSpec,
@@ -114,7 +115,7 @@
interactiveNotifications = it[interactiveNotificationsKey] as Boolean,
showMediaPlayer = it[showMediaPlayerKey] as Boolean,
isFullscreen = it[isFullscreenKey] as Boolean,
- canChangeScene = it[canChangeSceneKey] as Boolean,
+ canChangeSceneOrOverlays = it[canChangeSceneOrOverlaysKey] as Boolean,
transitionInterceptionThreshold =
it[transitionInterceptionThresholdKey] as Float,
springConfigurations =
@@ -350,11 +351,24 @@
// Can change scene.
Checkbox(
- label = "Can change scene",
- checked = configuration.canChangeScene,
+ label = "Can change scene or overlays",
+ checked = configuration.canChangeSceneOrOverlays,
onCheckedChange = {
onConfigurationChange(
- configuration.copy(canChangeScene = !configuration.canChangeScene)
+ configuration.copy(
+ canChangeSceneOrOverlays = !configuration.canChangeSceneOrOverlays
+ )
+ )
+ },
+ )
+
+ // Overlays.
+ Checkbox(
+ label = "Overlays",
+ checked = configuration.enableOverlays,
+ onCheckedChange = {
+ onConfigurationChange(
+ configuration.copy(enableOverlays = !configuration.enableOverlays)
)
},
)
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/HorizontalHalfScreen.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/HorizontalHalfScreen.kt
new file mode 100644
index 0000000..9fe8df2
--- /dev/null
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/HorizontalHalfScreen.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.demo
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import com.android.compose.animation.scene.SwipeSource
+import com.android.compose.animation.scene.SwipeSourceDetector
+
+/** A [SwipeSource] for the left or right half of the device. */
+enum class HorizontalHalfScreen(private val onResolve: (LayoutDirection) -> Resolved) :
+ SwipeSource {
+ Left(onResolve = { Resolved.Left }),
+ Right(onResolve = { Resolved.Right }),
+ Start(onResolve = { if (it == LayoutDirection.Ltr) Resolved.Left else Resolved.Right }),
+ End(onResolve = { if (it == LayoutDirection.Ltr) Resolved.Right else Resolved.Left });
+
+ override fun resolve(layoutDirection: LayoutDirection): SwipeSource.Resolved {
+ return onResolve(layoutDirection)
+ }
+
+ enum class Resolved : SwipeSource.Resolved {
+ Left,
+ Right,
+ }
+}
+
+object HorizontalHalfScreenDetector : SwipeSourceDetector {
+ override fun source(
+ layoutSize: IntSize,
+ position: IntOffset,
+ density: Density,
+ orientation: Orientation,
+ ): SwipeSource.Resolved {
+ return if (position.x < layoutSize.width / 2) {
+ HorizontalHalfScreen.Resolved.Left
+ } else {
+ HorizontalHalfScreen.Resolved.Right
+ }
+ }
+}
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/Launcher.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/Launcher.kt
index c2b79a0..e2cc555 100644
--- a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/Launcher.kt
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/Launcher.kt
@@ -38,11 +38,26 @@
import com.android.compose.grid.VerticalGrid
object Launcher {
- fun userActions(shadeScene: SceneKey): Map<UserAction, UserActionResult> {
- return mapOf(
- Swipe.Down to shadeScene,
- Swipe(SwipeDirection.Down, pointerCount = 2) to Scenes.QuickSettings,
- )
+ fun userActions(
+ shadeScene: SceneKey,
+ configuration: DemoConfiguration,
+ ): Map<UserAction, UserActionResult> {
+ return buildList {
+ if (configuration.enableOverlays) {
+ add(
+ Swipe(SwipeDirection.Down, fromSource = HorizontalHalfScreen.Start) to
+ UserActionResult.ShowOverlay(Overlays.QuickSettings)
+ )
+ add(
+ Swipe(SwipeDirection.Down, fromSource = HorizontalHalfScreen.End) to
+ UserActionResult.ShowOverlay(Overlays.Notifications)
+ )
+ } else {
+ add(Swipe.Down to shadeScene)
+ add(Swipe(SwipeDirection.Down, pointerCount = 2) to Scenes.QuickSettings)
+ }
+ }
+ .toMap()
}
object Elements {
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/Lockscreen.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/Lockscreen.kt
index d50bd10..77fbf8c 100644
--- a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/Lockscreen.kt
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/Lockscreen.kt
@@ -48,16 +48,35 @@
isLockscreenDismissable: Boolean,
shadeScene: SceneKey,
requiresFullDistanceSwipeToShade: Boolean,
+ configuration: DemoConfiguration,
fastSwipeToQuickSettings: Boolean = true,
): Map<UserAction, UserActionResult> {
return buildList {
- add(
- Swipe.Down to
- UserActionResult(
- shadeScene,
- requiresFullDistanceSwipe = requiresFullDistanceSwipeToShade,
- )
- )
+ if (configuration.enableOverlays) {
+ add(
+ Swipe(SwipeDirection.Down, fromSource = HorizontalHalfScreen.Start) to
+ UserActionResult.ShowOverlay(
+ Overlays.QuickSettings,
+ requiresFullDistanceSwipe = requiresFullDistanceSwipeToShade,
+ )
+ )
+ add(
+ Swipe(SwipeDirection.Down, fromSource = HorizontalHalfScreen.End) to
+ UserActionResult.ShowOverlay(
+ Overlays.Notifications,
+ requiresFullDistanceSwipe = requiresFullDistanceSwipeToShade,
+ )
+ )
+ } else {
+ add(
+ Swipe.Down to
+ UserActionResult(
+ shadeScene,
+ requiresFullDistanceSwipe = requiresFullDistanceSwipeToShade,
+ )
+ )
+ }
+
add(Swipe.Start to Scenes.StubEnd)
add(Swipe.End to Scenes.StubStart)
add(
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/MediaPlayer.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/MediaPlayer.kt
index 4044bd7..98c62d0 100644
--- a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/MediaPlayer.kt
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/MediaPlayer.kt
@@ -79,6 +79,7 @@
Scenes.Shade,
Scenes.SplitShade,
Scenes.QuickSettings,
+ Overlays.QuickSettings,
)
override fun contentDuringTransition(
@@ -120,6 +121,7 @@
Scenes.SplitShade
transition.isTransitioningBetween(Scenes.Lockscreen, Scenes.QuickSettings) ->
Scenes.QuickSettings
+ transition.isTransitioningFromOrTo(Overlays.QuickSettings) -> Overlays.QuickSettings
else -> pickSingleContentIn(contents, transition, element)
}
}
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/NotificationShade.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/NotificationShade.kt
new file mode 100644
index 0000000..cf0b6ae
--- /dev/null
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/NotificationShade.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.demo
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserActionResult
+
+object NotificationShade {
+ object Elements {
+ val Root = ElementKey("NotificationShadeRoot")
+ val Content = ElementKey("NotificationShadeContent")
+ }
+
+ val UserActions =
+ mapOf(
+ Back to UserActionResult.HideOverlay(Overlays.Notifications),
+ Swipe.Up to UserActionResult.HideOverlay(Overlays.Notifications),
+ Swipe.Left to UserActionResult.ReplaceByOverlay(Overlays.QuickSettings),
+ )
+}
+
+@Composable
+fun SceneScope.NotificationShade(
+ notificationList: @Composable SceneScope.() -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ PartialShade(
+ modifier.element(NotificationShade.Elements.Root),
+
+ // The notification list already applies some padding.
+ innerPadding = PaddingValues(0.dp),
+ ) {
+ Column(Modifier.element(NotificationShade.Elements.Content)) { notificationList() }
+ }
+}
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/PartialShade.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/PartialShade.kt
new file mode 100644
index 0000000..a05248f
--- /dev/null
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/PartialShade.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.demo
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.LowestZIndexContentPicker
+import com.android.compose.animation.scene.SceneScope
+
+object PartialShade {
+ object Elements {
+ val Background =
+ ElementKey("PartialShadeBackground", contentPicker = LowestZIndexContentPicker)
+ }
+
+ object Colors {
+ val Background
+ @Composable get() = Shade.Colors.Scrim
+ }
+
+ object Shapes {
+ val Background = RoundedCornerShape(Shade.Dimensions.ScrimCornerSize)
+ }
+}
+
+@Composable
+fun SceneScope.PartialShade(
+ modifier: Modifier = Modifier,
+ outerPadding: PaddingValues = PaddingValues(16.dp),
+ innerPadding: PaddingValues = PaddingValues(16.dp),
+ content: @Composable BoxScope.() -> Unit,
+) {
+ Box(modifier.fillMaxWidth(0.5f).fillMaxHeight().padding(outerPadding)) {
+ Box(
+ Modifier.element(PartialShade.Elements.Background)
+ .matchParentSize()
+ .background(PartialShade.Colors.Background, PartialShade.Shapes.Background)
+ )
+
+ Box(Modifier.padding(innerPadding), content = content)
+ }
+}
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/QuickSettingsShade.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/QuickSettingsShade.kt
new file mode 100644
index 0000000..e222bac
--- /dev/null
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/QuickSettingsShade.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.demo
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserActionResult
+
+object QuickSettingsShade {
+ object Elements {
+ val Root = ElementKey("QuickSettingsShadeContentRoot")
+ val Content = ElementKey("QuickSettingsShadeContent")
+ }
+
+ val UserActions =
+ mapOf(
+ Back to UserActionResult.HideOverlay(Overlays.QuickSettings),
+ Swipe.Up to UserActionResult.HideOverlay(Overlays.QuickSettings),
+ Swipe.Right to UserActionResult.ReplaceByOverlay(Overlays.Notifications),
+ )
+}
+
+@Composable
+fun SceneScope.QuickSettingsShade(
+ mediaPlayer: @Composable (SceneScope.() -> Unit)?,
+ modifier: Modifier = Modifier,
+) {
+ PartialShade(modifier.element(QuickSettingsShade.Elements.Root)) {
+ Column(Modifier.element(QuickSettingsShade.Elements.Content)) {
+ Clock(MaterialTheme.colorScheme.onSurfaceVariant)
+ if (mediaPlayer != null) {
+ mediaPlayer()
+ }
+ }
+ }
+}
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/Shade.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/Shade.kt
index 4745500..8e12f36 100644
--- a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/Shade.kt
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/Shade.kt
@@ -390,7 +390,7 @@
@Composable
private fun SceneScope.Scrim(
- notificationList: @Composable (SceneScope.() -> Unit),
+ notificationList: @Composable SceneScope.() -> Unit,
shouldPunchHoleBehindScrim: Boolean,
scrimMinTopPadding: Dp,
modifier: Modifier = Modifier,
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/SplitLockscreen.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/SplitLockscreen.kt
index 6084293..a819e67 100644
--- a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/SplitLockscreen.kt
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/SplitLockscreen.kt
@@ -31,18 +31,23 @@
import com.android.compose.animation.scene.SceneScope
object SplitLockscreen {
- fun userActions(isLockscreenDismissable: Boolean, shadeScene: SceneKey) =
+ fun userActions(
+ isLockscreenDismissable: Boolean,
+ shadeScene: SceneKey,
+ configuration: DemoConfiguration,
+ ) =
Lockscreen.userActions(
isLockscreenDismissable,
shadeScene,
requiresFullDistanceSwipeToShade = false,
+ configuration,
fastSwipeToQuickSettings = false,
)
}
@Composable
fun SceneScope.SplitLockscreen(
- notificationList: @Composable (SceneScope.() -> Unit),
+ notificationList: @Composable SceneScope.() -> Unit,
mediaPlayer: @Composable (SceneScope.() -> Unit)?,
isDismissable: Boolean,
onToggleDismissable: () -> Unit,
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/SplitShade.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/SplitShade.kt
index 11e3f35..3f0504c 100644
--- a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/SplitShade.kt
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/SplitShade.kt
@@ -67,7 +67,7 @@
@Composable
fun SceneScope.SplitShade(
- notificationList: @Composable (SceneScope.() -> Unit),
+ notificationList: @Composable SceneScope.() -> Unit,
mediaPlayer: @Composable (SceneScope.() -> Unit)?,
quickSettingsTiles: List<QuickSettingsTileViewModel>,
nQuickSettingsRows: Int,
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/SystemUi.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/SystemUi.kt
index 2c26b48..0dcab7d 100644
--- a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/SystemUi.kt
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/SystemUi.kt
@@ -67,6 +67,7 @@
import androidx.compose.runtime.saveable.SaverScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.toComposeRect
@@ -83,8 +84,10 @@
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import androidx.window.layout.WindowMetricsCalculator
+import com.android.compose.animation.scene.DefaultEdgeDetector
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
+import com.android.compose.animation.scene.OverlayKey
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneTransitionLayout
@@ -156,6 +159,11 @@
}
}
+object Overlays {
+ val Notifications = OverlayKey("NotificationsOverlay")
+ val QuickSettings = OverlayKey("QuickSettingsOverlay")
+}
+
/** A [Saver] that restores a [MutableSceneTransitionLayoutState] to its previous [currentScene]. */
class MutableSceneTransitionLayoutSaver(
private val sceneSaver: Scenes.SceneSaver,
@@ -285,7 +293,7 @@
val canChangeScene =
remember(configuration) {
{ scene: SceneKey ->
- if (configuration.canChangeScene) {
+ if (configuration.canChangeSceneOrOverlays) {
maybeUpdateLockscreenDismissed(scene)
true
} else {
@@ -304,7 +312,13 @@
)
}
val layoutState =
- rememberSaveable(transitions, canChangeScene, enableInterruptions, saver = stateSaver) {
+ rememberSaveable(
+ transitions,
+ canChangeScene,
+ enableInterruptions,
+ configuration,
+ saver = stateSaver,
+ ) {
val initialScene =
initialScene?.let {
Scenes.ensureCorrectScene(
@@ -318,6 +332,9 @@
initialScene,
transitions,
canChangeScene = canChangeScene,
+ canShowOverlay = { configuration.canChangeSceneOrOverlays },
+ canHideOverlay = { configuration.canChangeSceneOrOverlays },
+ canReplaceOverlay = { _, _ -> configuration.canChangeSceneOrOverlays },
enableInterruptions = enableInterruptions,
)
}
@@ -385,6 +402,21 @@
.forEach { (scene, name) ->
Button(onClick = { onChangeScene(scene) }) { Text(name) }
}
+
+ listOf(Overlays.Notifications to "NS", Overlays.QuickSettings to "QSS")
+ .forEach { (overlay, name) ->
+ Button(
+ onClick = {
+ if (layoutState.currentOverlays.contains(overlay)) {
+ layoutState.hideOverlay(overlay, coroutineScope)
+ } else {
+ layoutState.showOverlay(overlay, coroutineScope)
+ }
+ }
+ ) {
+ Text(name)
+ }
+ }
}
}
}
@@ -430,8 +462,11 @@
// Make this layout accessible to UiAutomator.
Modifier.semantics { testTagsAsResourceId = true }
.testTag("SystemUiSceneTransitionLayout"),
+ swipeSourceDetector =
+ if (configuration.enableOverlays) HorizontalHalfScreenDetector
+ else DefaultEdgeDetector,
) {
- scene(Scenes.Launcher, Launcher.userActions(shadeScene)) {
+ scene(Scenes.Launcher, Launcher.userActions(shadeScene, configuration)) {
Launcher(launcherColumns)
}
scene(
@@ -446,6 +481,7 @@
ToggleableState.Indeterminate ->
configuration.interactiveNotifications
},
+ configuration,
),
) {
Lockscreen(
@@ -465,7 +501,11 @@
}
scene(
Scenes.SplitLockscreen,
- SplitLockscreen.userActions(isLockscreenDismissable, shadeScene),
+ SplitLockscreen.userActions(
+ isLockscreenDismissable,
+ shadeScene,
+ configuration,
+ ),
) {
SplitLockscreen(
notificationList = {
@@ -554,9 +594,31 @@
::onPowerButtonClicked,
)
}
+
scene(Scenes.AlwaysOnDisplay) {
AlwaysOnDisplay(Modifier.clickable { onChangeScene(lockscreenScene) })
}
+
+ overlay(
+ Overlays.Notifications,
+ userActions = NotificationShade.UserActions,
+ alignment = Alignment.TopEnd,
+ ) {
+ NotificationShade(
+ notificationList = {
+ NotificationList(
+ maxNotificationCount = configuration.notificationsInShade
+ )
+ }
+ )
+ }
+ overlay(
+ Overlays.QuickSettings,
+ userActions = QuickSettingsShade.UserActions,
+ alignment = Alignment.TopStart,
+ ) {
+ QuickSettingsShade(mediaPlayer)
+ }
}
}
}
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/notification/DemoNotifications.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/notification/DemoNotifications.kt
index 0b6a00c..7999782 100644
--- a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/notification/DemoNotifications.kt
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/notification/DemoNotifications.kt
@@ -26,6 +26,7 @@
import com.android.compose.animation.scene.SceneTransitions
import com.android.compose.animation.scene.StaticElementContentPicker
import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.compose.animation.scene.demo.Overlays
import com.android.compose.animation.scene.demo.Scenes
import com.android.compose.animation.scene.demo.SpringConfiguration
import com.android.compose.animation.scene.demo.transitions.ToShadeScrimFadeEndFraction
@@ -71,7 +72,13 @@
private object NotificationContentPicker : StaticElementContentPicker {
override val contents =
- setOf(Scenes.Lockscreen, Scenes.Shade, Scenes.SplitLockscreen, Scenes.SplitShade)
+ setOf(
+ Scenes.Lockscreen,
+ Scenes.Shade,
+ Scenes.SplitLockscreen,
+ Scenes.SplitShade,
+ Overlays.Notifications,
+ )
override fun contentDuringTransition(
element: ElementKey,
@@ -100,6 +107,7 @@
Scenes.SplitLockscreen
}
}
+ transition.isTransitioningFromOrTo(Overlays.Notifications) -> Overlays.Notifications
else -> pickSingleContentIn(contents, transition, element)
}
}
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/transitions/NotificationShadeTransitions.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/transitions/NotificationShadeTransitions.kt
new file mode 100644
index 0000000..e5c37aa
--- /dev/null
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/transitions/NotificationShadeTransitions.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.demo.transitions
+
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.SceneTransitionsBuilder
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.compose.animation.scene.demo.NotificationShade
+import com.android.compose.animation.scene.demo.Overlays
+import com.android.compose.animation.scene.demo.notification.NotificationList
+
+fun SceneTransitionsBuilder.notificationShadeTransitions() {
+ to(Overlays.Notifications) {
+ spec = tween(500)
+ toNotificationShade()
+ }
+
+ from(Overlays.Notifications) {
+ spec = tween(500)
+ reversed { toNotificationShade() }
+ sharedElement(NotificationList.Elements.Notifications, enabled = false)
+ }
+
+ overscroll(Overlays.Notifications, Orientation.Vertical) {
+ translate(NotificationShade.Elements.Root, y = { absoluteDistance })
+ }
+
+ overscroll(Overlays.Notifications, Orientation.Horizontal) {
+ translate(NotificationShade.Elements.Root, x = { absoluteDistance })
+ }
+}
+
+private fun TransitionBuilder.toNotificationShade() {
+ translate(NotificationShade.Elements.Root, Edge.Top)
+ fractionRange(start = 0.5f) { fade(NotificationList.Elements.Notifications) }
+}
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/transitions/QuickSettingsShadeTransitions.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/transitions/QuickSettingsShadeTransitions.kt
new file mode 100644
index 0000000..ee83dd8
--- /dev/null
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/transitions/QuickSettingsShadeTransitions.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.demo.transitions
+
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.SceneTransitionsBuilder
+import com.android.compose.animation.scene.demo.Clock
+import com.android.compose.animation.scene.demo.MediaPlayer
+import com.android.compose.animation.scene.demo.NotificationShade
+import com.android.compose.animation.scene.demo.Overlays
+import com.android.compose.animation.scene.demo.PartialShade
+import com.android.compose.animation.scene.demo.QuickSettingsShade
+import com.android.compose.animation.scene.demo.notification.NotificationList
+
+fun SceneTransitionsBuilder.quickSettingsShadeTransitions() {
+ to(Overlays.QuickSettings) {
+ spec = tween(500)
+ translate(QuickSettingsShade.Elements.Root, Edge.Top)
+ }
+
+ from(Overlays.QuickSettings, to = Overlays.Notifications) {
+ spec = tween(500)
+
+ anchoredTranslate(NotificationShade.Elements.Content, PartialShade.Elements.Background)
+ anchoredTranslate(QuickSettingsShade.Elements.Content, PartialShade.Elements.Background)
+
+ fractionRange(end = 0.5f) {
+ fade(MediaPlayer.Elements.MediaPlayer)
+ fade(Clock.Elements.Clock)
+ }
+
+ fractionRange(start = 0.5f) { fade(NotificationList.Elements.Notifications) }
+ }
+
+ overscroll(Overlays.QuickSettings, Orientation.Vertical) {
+ translate(QuickSettingsShade.Elements.Root, y = { absoluteDistance })
+ }
+
+ overscroll(Overlays.QuickSettings, Orientation.Horizontal) {
+ translate(QuickSettingsShade.Elements.Root, x = { absoluteDistance })
+ }
+}
diff --git a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/transitions/SystemUiTransitions.kt b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/transitions/SystemUiTransitions.kt
index ceb932a..89dfc41 100644
--- a/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/transitions/SystemUiTransitions.kt
+++ b/samples/SceneTransitionLayoutDemo/src/com/android/compose/animation/scene/demo/transitions/SystemUiTransitions.kt
@@ -48,6 +48,8 @@
lockscreenTransitions(configuration)
bouncerTransitions(configuration)
launcherTransitions()
+ notificationShadeTransitions()
+ quickSettingsShadeTransitions()
}
object DemoInterruptionHandler : InterruptionHandler {