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

Commit 6bbac533 authored by Steve Elliott's avatar Steve Elliott
Browse files

[flexiglass] Preserve notif composable state across scenes

Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT
Test:
  1. Go to shade
  2. Scroll notifications
  3. Tap a notification to go to bouncer
  4. Return to shade, observe scroll state is preserved

Change-Id: I9808a02d563ac406a6f15e896af669f7cae03a69
parent 08200af5
Loading
Loading
Loading
Loading
+10 −5
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.notifications.ui.composable

import android.util.Log
import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.Box
@@ -30,7 +31,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
@@ -40,7 +40,6 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
@@ -70,6 +69,8 @@ import com.android.compose.modifiers.height
import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.session.ui.composable.rememberSession
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.composable.ShadeHeader
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
@@ -156,6 +157,7 @@ fun SceneScope.ConstrainedNotificationStack(
 */
@Composable
fun SceneScope.NotificationScrollingStack(
    shadeSession: SaveableSession,
    viewModel: NotificationsPlaceholderViewModel,
    maxScrimTop: () -> Float,
    shouldPunchHoleBehindScrim: Boolean,
@@ -165,7 +167,10 @@ fun SceneScope.NotificationScrollingStack(
    val density = LocalDensity.current
    val screenCornerRadius = LocalScreenCornerRadius.current
    val scrimCornerRadius = dimensionResource(R.dimen.notification_scrim_corner_radius)
    val scrollState = rememberScrollState()
    val scrollState =
        shadeSession.rememberSaveableSession(saver = ScrollState.Saver, key = null) {
            ScrollState(initial = 0)
        }
    val syntheticScroll = viewModel.syntheticScroll.collectAsState(0f)
    val isCurrentGestureOverscroll = viewModel.isCurrentGestureOverscroll.collectAsState(false)
    val expansionFraction by viewModel.expandFraction.collectAsState(0f)
@@ -184,7 +189,7 @@ fun SceneScope.NotificationScrollingStack(
    // When fully expanded (scrimOffset = minScrimOffset), its top bound is at minScrimStartY,
    // which is equal to the height of the Shade Header. Thus, when the scrim is fully expanded, the
    // entire height of the scrim is visible on screen.
    val scrimOffset = remember { Animatable(0f) }
    val scrimOffset = shadeSession.rememberSession { Animatable(0f) }

    // set the bounds to null when the scrim disappears
    DisposableEffect(Unit) { onDispose { viewModel.onScrimBoundsChanged(null) } }
@@ -303,7 +308,7 @@ fun SceneScope.NotificationScrollingStack(
                            isExternalOverscrollGesture = { isCurrentGestureOverscroll.value }
                        )
                        .nestedScroll(
                            remember(
                            shadeSession.rememberSession(
                                scrimOffset,
                                maxScrimTop,
                                minScrimTop,
+43 −0
Original line number Diff line number Diff line
/*
 * 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.systemui.notifications.ui.composable

import androidx.compose.runtime.Composable
import androidx.compose.runtime.saveable.Saver
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.session.shared.SessionStorage
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.session.ui.composable.Session
import dagger.Module
import dagger.Provides

@Module
object NotificationsShadeSessionModule {
    @Provides @SysUISingleton fun provideShadeSessionStorage(): SessionStorage = SessionStorage()

    @Provides
    fun provideShadeSession(storage: SessionStorage): SaveableSession =
        object : SaveableSession, Session by Session(storage) {
            @Composable
            override fun <T : Any> rememberSaveableSession(
                vararg inputs: Any?,
                saver: Saver<T, out Any>,
                key: String?,
                init: () -> T
            ): T = rememberSession(key, inputs = inputs, init = init)
        }
}
+5 −0
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ import com.android.systemui.notifications.ui.composable.NotificationScrollingSta
import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility
import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel
import com.android.systemui.res.R
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.ui.composable.CollapsedShadeHeader
@@ -103,6 +104,7 @@ class QuickSettingsScene
@Inject
constructor(
    @Application private val applicationScope: CoroutineScope,
    private val shadeSession: SaveableSession,
    private val viewModel: QuickSettingsSceneViewModel,
    private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
    private val tintedIconManagerFactory: TintedIconManager.Factory,
@@ -133,6 +135,7 @@ constructor(
            mediaCarouselController = mediaCarouselController,
            mediaHost = mediaHost,
            modifier = modifier,
            shadeSession = shadeSession,
        )
    }
}
@@ -147,6 +150,7 @@ private fun SceneScope.QuickSettingsScene(
    mediaCarouselController: MediaCarouselController,
    mediaHost: MediaHost,
    modifier: Modifier = Modifier,
    shadeSession: SaveableSession,
) {
    val brightnessMirrorShowing by viewModel.brightnessMirrorViewModel.isShowing.collectAsState()
    val contentAlpha by
@@ -347,6 +351,7 @@ private fun SceneScope.QuickSettingsScene(
        }
        NotificationScrollingStack(
            viewModel = notificationsPlaceholderViewModel,
            shadeSession = shadeSession,
            maxScrimTop = { screenHeight },
            shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
            modifier =
+11 −0
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibi
import com.android.systemui.qs.ui.composable.BrightnessMirror
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.res.R
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
@@ -119,6 +120,7 @@ object Shade {
class ShadeScene
@Inject
constructor(
    private val shadeSession: SaveableSession,
    private val viewModel: ShadeSceneViewModel,
    private val tintedIconManagerFactory: TintedIconManager.Factory,
    private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
@@ -126,6 +128,7 @@ constructor(
    private val mediaCarouselController: MediaCarouselController,
    @Named(QUICK_QS_PANEL) private val mediaHost: MediaHost,
) : ComposableScene {

    override val key = Scenes.Shade

    override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
@@ -143,6 +146,7 @@ constructor(
            mediaCarouselController = mediaCarouselController,
            mediaHost = mediaHost,
            modifier = modifier,
            shadeSession = shadeSession,
        )

    init {
@@ -161,6 +165,7 @@ private fun SceneScope.ShadeScene(
    mediaCarouselController: MediaCarouselController,
    mediaHost: MediaHost,
    modifier: Modifier = Modifier,
    shadeSession: SaveableSession,
) {
    val shadeMode by viewModel.shadeMode.collectAsState()
    when (shadeMode) {
@@ -173,6 +178,7 @@ private fun SceneScope.ShadeScene(
                mediaCarouselController = mediaCarouselController,
                mediaHost = mediaHost,
                modifier = modifier,
                shadeSession = shadeSession,
            )
        is ShadeMode.Split ->
            SplitShade(
@@ -183,6 +189,7 @@ private fun SceneScope.ShadeScene(
                mediaCarouselController = mediaCarouselController,
                mediaHost = mediaHost,
                modifier = modifier,
                shadeSession = shadeSession,
            )
        is ShadeMode.Dual -> error("Dual shade is not yet implemented!")
    }
@@ -197,6 +204,7 @@ private fun SceneScope.SingleShade(
    mediaCarouselController: MediaCarouselController,
    mediaHost: MediaHost,
    modifier: Modifier = Modifier,
    shadeSession: SaveableSession,
) {
    val maxNotifScrimTop = remember { mutableStateOf(0f) }
    val tileSquishiness by
@@ -268,6 +276,7 @@ private fun SceneScope.SingleShade(
                    },
                    {
                        NotificationScrollingStack(
                            shadeSession = shadeSession,
                            viewModel = viewModel.notifications,
                            maxScrimTop = { maxNotifScrimTop.value },
                            shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
@@ -301,6 +310,7 @@ private fun SceneScope.SplitShade(
    mediaCarouselController: MediaCarouselController,
    mediaHost: MediaHost,
    modifier: Modifier = Modifier,
    shadeSession: SaveableSession,
) {
    val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState()
    val isCustomizerShowing by viewModel.qsSceneAdapter.isCustomizerShowing.collectAsState()
@@ -450,6 +460,7 @@ private fun SceneScope.SplitShade(
                }

                NotificationScrollingStack(
                    shadeSession = shadeSession,
                    viewModel = viewModel.notifications,
                    maxScrimTop = { 0f },
                    shouldPunchHoleBehindScrim = false,
+2 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.scene

import com.android.systemui.CoreStartable
import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.shared.model.SceneContainerConfig
@@ -35,6 +36,7 @@ import dagger.multibindings.IntoMap
            EmptySceneModule::class,
            GoneSceneModule::class,
            NotificationsShadeSceneModule::class,
            NotificationsShadeSessionModule::class,
            QuickSettingsSceneModule::class,
            ShadeSceneModule::class,
        ],
Loading