Loading packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +10 −7 Original line number Diff line number Diff line Loading @@ -66,6 +66,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.NestedScrollBehavior import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.thenIf Loading @@ -90,7 +91,8 @@ object Notifications { object Elements { val NotificationScrim = ElementKey("NotificationScrim") val NotificationStackPlaceholder = ElementKey("NotificationStackPlaceholder") val HeadsUpNotificationPlaceholder = ElementKey("HeadsUpNotificationPlaceholder") val HeadsUpNotificationPlaceholder = ElementKey("HeadsUpNotificationPlaceholder", scenePicker = LowestZIndexScenePicker) val ShelfSpace = ElementKey("ShelfSpace") } Loading @@ -113,10 +115,10 @@ fun SceneScope.HeadsUpNotificationSpace( modifier: Modifier = Modifier, isPeekFromBottom: Boolean = false, ) { Element( Notifications.Elements.HeadsUpNotificationPlaceholder, Box( modifier = modifier .element(Notifications.Elements.HeadsUpNotificationPlaceholder) .fillMaxWidth() .notificationHeadsUpHeight(stackScrollView) .debugBackground(viewModel, DEBUG_HUN_COLOR) Loading @@ -130,9 +132,7 @@ fun SceneScope.HeadsUpNotificationSpace( // Note: boundsInWindow doesn't scroll off the screen stackScrollView.setHeadsUpTop(boundsInWindow.top) } ) { content {} } ) } /** Adds the space where notification stack should appear in the scene. */ Loading Loading @@ -177,6 +177,7 @@ fun SceneScope.NotificationScrollingStack( shouldPunchHoleBehindScrim: Boolean, shouldFillMaxSize: Boolean = true, shouldReserveSpaceForNavBar: Boolean = true, shouldIncludeHeadsUpSpace: Boolean = true, shadeMode: ShadeMode, modifier: Modifier = Modifier, ) { Loading Loading @@ -366,9 +367,11 @@ fun SceneScope.NotificationScrollingStack( .onSizeChanged { size -> stackHeight.intValue = size.height }, ) } if (shouldIncludeHeadsUpSpace) { HeadsUpNotificationSpace(stackScrollView = stackScrollView, viewModel = viewModel) } } } /** * This may be added to the lockscreen to provide a space to the start of the lock icon where the Loading packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +16 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.rememberScrollState Loading @@ -62,6 +63,7 @@ import androidx.compose.ui.layout.layoutId import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.colorResource import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.SceneScope Loading @@ -82,6 +84,7 @@ import com.android.systemui.media.controls.ui.controller.MediaCarouselController import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.dagger.MediaModule import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace import com.android.systemui.notifications.ui.composable.NotificationScrollingStack import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQS Loading @@ -90,6 +93,7 @@ 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 import com.android.systemui.shade.ui.composable.CollapsedShadeHeader import com.android.systemui.shade.ui.composable.ExpandedShadeHeader import com.android.systemui.shade.ui.composable.Shade Loading @@ -102,6 +106,7 @@ import com.android.systemui.statusbar.phone.ui.TintedIconManager import dagger.Lazy import javax.inject.Inject import javax.inject.Named import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.stateIn Loading Loading @@ -400,5 +405,16 @@ private fun SceneScope.QuickSettingsScene( modifier = Modifier.align(Alignment.BottomCenter), isPeekFromBottom = true, ) NotificationScrollingStack( shadeSession = shadeSession, stackScrollView = notificationStackScrollView, viewModel = notificationsPlaceholderViewModel, maxScrimTop = { screenHeight }, shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim, shouldIncludeHeadsUpSpace = false, shadeMode = ShadeMode.Single, modifier = Modifier.fillMaxWidth().offset { IntOffset(x = 0, y = screenHeight.roundToInt()) }, ) } } packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt +48 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_DELAYED_STACK_FADE_IN import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationScrollViewModel import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel import com.android.systemui.testKosmos Loading Loading @@ -204,6 +206,52 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { assertThat(expandFraction).isEqualTo(1f) } fakeSceneDataSource.unpause(expectedScene = Scenes.QuickSettings) assertThat(expandFraction).isEqualTo(1f) assertThat(isScrollable).isFalse() } @Test fun shadeExpansion_goneToQs() = testScope.runTest { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(currentScene = Scenes.Gone) ) sceneInteractor.setTransitionState(transitionState) val expandFraction by collectLastValue(scrollViewModel.expandFraction) assertThat(expandFraction).isEqualTo(0f) fakeSceneDataSource.changeScene(toScene = Scenes.Gone) val isScrollable by collectLastValue(scrollViewModel.isScrollable) assertThat(isScrollable).isFalse() fakeSceneDataSource.pause() sceneInteractor.changeScene(Scenes.QuickSettings, "reason") val transitionProgress = MutableStateFlow(0f) transitionState.value = ObservableTransitionState.Transition( fromScene = Scenes.Gone, toScene = Scenes.QuickSettings, currentScene = flowOf(Scenes.QuickSettings), progress = transitionProgress, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), ) val steps = 10 repeat(steps) { repetition -> val progress = (1f / steps) * (repetition + 1) transitionProgress.value = progress runCurrent() assertThat(expandFraction) .isEqualTo( (progress / EXPANSION_FOR_MAX_SCRIM_ALPHA - EXPANSION_FOR_DELAYED_STACK_FADE_IN) .coerceIn(0f, 1f) ) } fakeSceneDataSource.unpause(expectedScene = Scenes.QuickSettings) assertThat(expandFraction).isEqualTo(1f) assertThat(isScrollable).isFalse() Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt +4 −2 Original line number Diff line number Diff line Loading @@ -90,8 +90,10 @@ constructor( 1f } else if ( shadeMode != ShadeMode.Split && transitionState.fromScene in SceneFamilies.Home && transitionState.toScene in quickSettingsScene (transitionState.fromScene in SceneFamilies.Home && transitionState.toScene == quickSettingsScene) || (transitionState.fromScene == quickSettingsScene && transitionState.toScene in SceneFamilies.Home) ) { // during QS expansion, increase fraction at same rate as scrim alpha, // but start when scrim alpha is at EXPANSION_FOR_DELAYED_STACK_FADE_IN. Loading Loading
packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +10 −7 Original line number Diff line number Diff line Loading @@ -66,6 +66,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.NestedScrollBehavior import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.thenIf Loading @@ -90,7 +91,8 @@ object Notifications { object Elements { val NotificationScrim = ElementKey("NotificationScrim") val NotificationStackPlaceholder = ElementKey("NotificationStackPlaceholder") val HeadsUpNotificationPlaceholder = ElementKey("HeadsUpNotificationPlaceholder") val HeadsUpNotificationPlaceholder = ElementKey("HeadsUpNotificationPlaceholder", scenePicker = LowestZIndexScenePicker) val ShelfSpace = ElementKey("ShelfSpace") } Loading @@ -113,10 +115,10 @@ fun SceneScope.HeadsUpNotificationSpace( modifier: Modifier = Modifier, isPeekFromBottom: Boolean = false, ) { Element( Notifications.Elements.HeadsUpNotificationPlaceholder, Box( modifier = modifier .element(Notifications.Elements.HeadsUpNotificationPlaceholder) .fillMaxWidth() .notificationHeadsUpHeight(stackScrollView) .debugBackground(viewModel, DEBUG_HUN_COLOR) Loading @@ -130,9 +132,7 @@ fun SceneScope.HeadsUpNotificationSpace( // Note: boundsInWindow doesn't scroll off the screen stackScrollView.setHeadsUpTop(boundsInWindow.top) } ) { content {} } ) } /** Adds the space where notification stack should appear in the scene. */ Loading Loading @@ -177,6 +177,7 @@ fun SceneScope.NotificationScrollingStack( shouldPunchHoleBehindScrim: Boolean, shouldFillMaxSize: Boolean = true, shouldReserveSpaceForNavBar: Boolean = true, shouldIncludeHeadsUpSpace: Boolean = true, shadeMode: ShadeMode, modifier: Modifier = Modifier, ) { Loading Loading @@ -366,9 +367,11 @@ fun SceneScope.NotificationScrollingStack( .onSizeChanged { size -> stackHeight.intValue = size.height }, ) } if (shouldIncludeHeadsUpSpace) { HeadsUpNotificationSpace(stackScrollView = stackScrollView, viewModel = viewModel) } } } /** * This may be added to the lockscreen to provide a space to the start of the lock icon where the Loading
packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +16 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.rememberScrollState Loading @@ -62,6 +63,7 @@ import androidx.compose.ui.layout.layoutId import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.colorResource import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.SceneScope Loading @@ -82,6 +84,7 @@ import com.android.systemui.media.controls.ui.controller.MediaCarouselController import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.dagger.MediaModule import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace import com.android.systemui.notifications.ui.composable.NotificationScrollingStack import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQS Loading @@ -90,6 +93,7 @@ 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 import com.android.systemui.shade.ui.composable.CollapsedShadeHeader import com.android.systemui.shade.ui.composable.ExpandedShadeHeader import com.android.systemui.shade.ui.composable.Shade Loading @@ -102,6 +106,7 @@ import com.android.systemui.statusbar.phone.ui.TintedIconManager import dagger.Lazy import javax.inject.Inject import javax.inject.Named import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.stateIn Loading Loading @@ -400,5 +405,16 @@ private fun SceneScope.QuickSettingsScene( modifier = Modifier.align(Alignment.BottomCenter), isPeekFromBottom = true, ) NotificationScrollingStack( shadeSession = shadeSession, stackScrollView = notificationStackScrollView, viewModel = notificationsPlaceholderViewModel, maxScrimTop = { screenHeight }, shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim, shouldIncludeHeadsUpSpace = false, shadeMode = ShadeMode.Single, modifier = Modifier.fillMaxWidth().offset { IntOffset(x = 0, y = screenHeight.roundToInt()) }, ) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt +48 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_DELAYED_STACK_FADE_IN import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationScrollViewModel import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel import com.android.systemui.testKosmos Loading Loading @@ -204,6 +206,52 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { assertThat(expandFraction).isEqualTo(1f) } fakeSceneDataSource.unpause(expectedScene = Scenes.QuickSettings) assertThat(expandFraction).isEqualTo(1f) assertThat(isScrollable).isFalse() } @Test fun shadeExpansion_goneToQs() = testScope.runTest { val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(currentScene = Scenes.Gone) ) sceneInteractor.setTransitionState(transitionState) val expandFraction by collectLastValue(scrollViewModel.expandFraction) assertThat(expandFraction).isEqualTo(0f) fakeSceneDataSource.changeScene(toScene = Scenes.Gone) val isScrollable by collectLastValue(scrollViewModel.isScrollable) assertThat(isScrollable).isFalse() fakeSceneDataSource.pause() sceneInteractor.changeScene(Scenes.QuickSettings, "reason") val transitionProgress = MutableStateFlow(0f) transitionState.value = ObservableTransitionState.Transition( fromScene = Scenes.Gone, toScene = Scenes.QuickSettings, currentScene = flowOf(Scenes.QuickSettings), progress = transitionProgress, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), ) val steps = 10 repeat(steps) { repetition -> val progress = (1f / steps) * (repetition + 1) transitionProgress.value = progress runCurrent() assertThat(expandFraction) .isEqualTo( (progress / EXPANSION_FOR_MAX_SCRIM_ALPHA - EXPANSION_FOR_DELAYED_STACK_FADE_IN) .coerceIn(0f, 1f) ) } fakeSceneDataSource.unpause(expectedScene = Scenes.QuickSettings) assertThat(expandFraction).isEqualTo(1f) assertThat(isScrollable).isFalse() Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt +4 −2 Original line number Diff line number Diff line Loading @@ -90,8 +90,10 @@ constructor( 1f } else if ( shadeMode != ShadeMode.Split && transitionState.fromScene in SceneFamilies.Home && transitionState.toScene in quickSettingsScene (transitionState.fromScene in SceneFamilies.Home && transitionState.toScene == quickSettingsScene) || (transitionState.fromScene == quickSettingsScene && transitionState.toScene in SceneFamilies.Home) ) { // during QS expansion, increase fraction at same rate as scrim alpha, // but start when scrim alpha is at EXPANSION_FOR_DELAYED_STACK_FADE_IN. Loading