Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit daf478a8 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[flexiglass] Shade scene view-models are SysUiViewModel

As per go/sysui-arch:summer-24

Bug: 354270224
Flag: com.android.systemui.scene_container
Test: new unit test, updated existing unit and integration tests
Test: manually verified that the shade scene can navigate from/to adjacent scenes
and that its elements seem to animate and render correctly

Change-Id: I2e1fb128555006a47743b1b6e89b3aabc56c8a0e
parent bba1cd5a
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -276,6 +276,7 @@ fun SceneScope.NotificationScrollingStack(
    shouldReserveSpaceForNavBar: Boolean = true,
    shouldIncludeHeadsUpSpace: Boolean = true,
    shadeMode: ShadeMode,
    onEmptySpaceClick: (() -> Unit)? = null,
    modifier: Modifier = Modifier,
) {
    val coroutineScope = rememberCoroutineScope()
@@ -328,8 +329,6 @@ fun SceneScope.NotificationScrollingStack(
    // The height of the scrim visible on screen when it is in its resting (collapsed) state.
    val minVisibleScrimHeight: () -> Float = { screenHeight - maxScrimTop() }

    val isClickable by viewModel.isClickable.collectAsStateWithLifecycle()

    // we are not scrolled to the top unless the scrim is at its maximum offset.
    LaunchedEffect(viewModel, scrimOffset) {
        snapshotFlow { scrimOffset.value >= 0f }
@@ -437,8 +436,8 @@ fun SceneScope.NotificationScrollingStack(
                        )
                    )
                }
                .thenIf(isClickable) {
                    Modifier.clickable(onClick = { viewModel.onEmptySpaceClicked() })
                .thenIf(onEmptySpaceClick != null) {
                    Modifier.clickable(onClick = { onEmptySpaceClick?.invoke() })
                }
    ) {
        // Creates a cutout in the background scrim in the shape of the notifications scrim.
+23 −5
Original line number Diff line number Diff line
@@ -20,15 +20,19 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneActionsViewModel
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneContentViewModel
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
@@ -50,8 +54,9 @@ import kotlinx.coroutines.flow.Flow
class NotificationsShadeScene
@Inject
constructor(
    sceneViewModel: NotificationsShadeSceneViewModel,
    private val overlayShadeViewModel: OverlayShadeViewModel,
    private val contentViewModelFactory: NotificationsShadeSceneContentViewModel.Factory,
    private val actionsViewModelFactory: NotificationsShadeSceneActionsViewModel.Factory,
    private val overlayShadeViewModelFactory: OverlayShadeViewModel.Factory,
    private val shadeHeaderViewModel: ShadeHeaderViewModel,
    private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
    private val tintedIconManagerFactory: TintedIconManager.Factory,
@@ -64,16 +69,27 @@ constructor(

    override val key = Scenes.NotificationsShade

    private val actionsViewModel: NotificationsShadeSceneActionsViewModel by lazy {
        actionsViewModelFactory.create()
    }

    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
        sceneViewModel.destinationScenes
        actionsViewModel.actions

    override suspend fun activate() {
        actionsViewModel.activate()
    }

    @Composable
    override fun SceneScope.Content(
        modifier: Modifier,
    ) {
        val viewModel = rememberViewModel { contentViewModelFactory.create() }
        val isEmptySpaceClickable by viewModel.isEmptySpaceClickable.collectAsStateWithLifecycle()

        OverlayShade(
            modifier = modifier,
            viewModel = overlayShadeViewModel,
            viewModelFactory = overlayShadeViewModelFactory,
            lockscreenContent = lockscreenContent,
        ) {
            Column {
@@ -94,6 +110,8 @@ constructor(
                    shouldFillMaxSize = false,
                    shouldReserveSpaceForNavBar = false,
                    shadeMode = ShadeMode.Dual,
                    onEmptySpaceClick =
                        viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
                    modifier = Modifier.fillMaxWidth(),
                )

+12 −4
Original line number Diff line number Diff line
@@ -44,12 +44,14 @@ import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.qs.panels.ui.compose.EditMode
import com.android.systemui.qs.panels.ui.compose.TileGrid
import com.android.systemui.qs.ui.composable.QuickSettingsShade.Transitions.QuickSettingsLayoutEnter
import com.android.systemui.qs.ui.composable.QuickSettingsShade.Transitions.QuickSettingsLayoutExit
import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneActionsViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneContentViewModel
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
@@ -66,7 +68,8 @@ import kotlinx.coroutines.flow.Flow
class QuickSettingsShadeScene
@Inject
constructor(
    private val viewModel: QuickSettingsShadeSceneViewModel,
    private val actionsViewModelFactory: QuickSettingsShadeSceneActionsViewModel.Factory,
    private val contentViewModelFactory: QuickSettingsShadeSceneContentViewModel.Factory,
    private val lockscreenContent: Lazy<Optional<LockscreenContent>>,
    private val shadeHeaderViewModel: ShadeHeaderViewModel,
    private val tintedIconManagerFactory: TintedIconManager.Factory,
@@ -76,15 +79,20 @@ constructor(

    override val key = Scenes.QuickSettingsShade

    private val actionsViewModel: QuickSettingsShadeSceneActionsViewModel by lazy {
        actionsViewModelFactory.create()
    }

    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
        viewModel.destinationScenes
        actionsViewModel.actions

    @Composable
    override fun SceneScope.Content(
        modifier: Modifier,
    ) {
        val viewModel = rememberViewModel { contentViewModelFactory.create() }
        OverlayShade(
            viewModel = viewModel.overlayShadeViewModel,
            viewModelFactory = viewModel.overlayShadeViewModelFactory,
            lockscreenContent = lockscreenContent,
            modifier = modifier,
        ) {
+3 −1
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ 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
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.shared.model.ShadeAlignment
import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
@@ -63,11 +64,12 @@ import java.util.Optional
/** The overlay shade renders a lightweight shade UI container on top of a background scene. */
@Composable
fun SceneScope.OverlayShade(
    viewModel: OverlayShadeViewModel,
    viewModelFactory: OverlayShadeViewModel.Factory,
    lockscreenContent: Lazy<Optional<LockscreenContent>>,
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
) {
    val viewModel = rememberViewModel { viewModelFactory.create() }
    val backgroundScene by viewModel.backgroundScene.collectAsStateWithLifecycle()

    Box(modifier) {
+23 −11
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.media.controls.ui.composable.MediaContentPicker
import com.android.systemui.media.controls.ui.composable.shouldElevateMedia
@@ -102,7 +103,8 @@ import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
import com.android.systemui.shade.ui.viewmodel.ShadeSceneActionsViewModel
import com.android.systemui.shade.ui.viewmodel.ShadeSceneContentViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import com.android.systemui.statusbar.phone.StatusBarLocation
@@ -145,7 +147,8 @@ class ShadeScene
constructor(
    private val shadeSession: SaveableSession,
    private val notificationStackScrollView: Lazy<NotificationScrollView>,
    private val viewModel: ShadeSceneViewModel,
    private val actionsViewModelFactory: ShadeSceneActionsViewModel.Factory,
    private val contentViewModelFactory: ShadeSceneContentViewModel.Factory,
    private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
    private val tintedIconManagerFactory: TintedIconManager.Factory,
    private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
@@ -157,12 +160,16 @@ constructor(

    override val key = Scenes.Shade

    private val actionsViewModel: ShadeSceneActionsViewModel by lazy {
        actionsViewModelFactory.create()
    }

    override suspend fun activate() {
        viewModel.activate()
        actionsViewModel.activate()
    }

    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
        viewModel.destinationScenes
        actionsViewModel.actions

    @Composable
    override fun SceneScope.Content(
@@ -170,7 +177,7 @@ constructor(
    ) =
        ShadeScene(
            notificationStackScrollView.get(),
            viewModel = viewModel,
            viewModel = rememberViewModel { contentViewModelFactory.create() },
            notificationsPlaceholderViewModel = notificationsPlaceholderViewModel,
            createTintedIconManager = tintedIconManagerFactory::create,
            createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
@@ -196,7 +203,7 @@ constructor(
@Composable
private fun SceneScope.ShadeScene(
    notificationStackScrollView: NotificationScrollView,
    viewModel: ShadeSceneViewModel,
    viewModel: ShadeSceneContentViewModel,
    notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
    createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
    createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
@@ -242,7 +249,7 @@ private fun SceneScope.ShadeScene(
@Composable
private fun SceneScope.SingleShade(
    notificationStackScrollView: NotificationScrollView,
    viewModel: ShadeSceneViewModel,
    viewModel: ShadeSceneContentViewModel,
    notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
    createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
    createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
@@ -261,7 +268,7 @@ private fun SceneScope.SingleShade(
            key = QuickSettings.SharedValues.TilesSquishiness,
            canOverflow = false
        )
    val isClickable by viewModel.isClickable.collectAsStateWithLifecycle()
    val isEmptySpaceClickable by viewModel.isEmptySpaceClickable.collectAsStateWithLifecycle()
    val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()

    val shouldPunchHoleBehindScrim =
@@ -299,9 +306,9 @@ private fun SceneScope.SingleShade(
                            horizontalAlignment = Alignment.CenterHorizontally,
                            modifier =
                                Modifier.fillMaxWidth()
                                    .thenIf(isClickable) {
                                    .thenIf(isEmptySpaceClickable) {
                                        Modifier.clickable(
                                            onClick = { viewModel.onContentClicked() }
                                            onClick = { viewModel.onEmptySpaceClicked() }
                                        )
                                    }
                                    .thenIf(cutoutLocation != CutoutLocation.CENTER) {
@@ -361,6 +368,8 @@ private fun SceneScope.SingleShade(
                            maxScrimTop = { maxNotifScrimTop.value },
                            shadeMode = ShadeMode.Single,
                            shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
                            onEmptySpaceClick =
                                viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
                        )
                    },
                )
@@ -407,7 +416,7 @@ private fun SceneScope.SingleShade(
@Composable
private fun SceneScope.SplitShade(
    notificationStackScrollView: NotificationScrollView,
    viewModel: ShadeSceneViewModel,
    viewModel: ShadeSceneContentViewModel,
    notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
    createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
    createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
@@ -481,6 +490,7 @@ private fun SceneScope.SplitShade(
        onDispose { notificationsPlaceholderViewModel.setAlphaForBrightnessMirror(1f) }
    }

    val isEmptySpaceClickable by viewModel.isEmptySpaceClickable.collectAsStateWithLifecycle()
    val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()

    val brightnessMirrorShowingModifier = Modifier.graphicsLayer { alpha = contentAlpha }
@@ -591,6 +601,8 @@ private fun SceneScope.SplitShade(
                    shouldPunchHoleBehindScrim = false,
                    shouldReserveSpaceForNavBar = false,
                    shadeMode = ShadeMode.Split,
                    onEmptySpaceClick =
                        viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
                    modifier =
                        Modifier.weight(1f)
                            .fillMaxHeight()
Loading