Loading packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt +10 −10 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.keyguard.ui.composable.LockscreenSceneBlueprintModul import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel import com.android.systemui.scene.ui.composable.Scene import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel import dagger.Binds import dagger.Module import dagger.Provides Loading @@ -35,12 +36,7 @@ import dagger.multibindings.IntoSet import javax.inject.Provider import kotlinx.coroutines.ExperimentalCoroutinesApi @Module( includes = [ LockscreenSceneBlueprintModule::class, ], ) @Module(includes = [LockscreenSceneBlueprintModule::class]) interface LockscreenSceneModule { @Binds @IntoSet fun lockscreenScene(scene: LockscreenScene): Scene Loading @@ -51,9 +47,7 @@ interface LockscreenSceneModule { @Provides @SysUISingleton @KeyguardRootView fun viewProvider( configurator: Provider<KeyguardViewConfigurator>, ): () -> View { fun viewProvider(configurator: Provider<KeyguardViewConfigurator>): () -> View { return { configurator.get().getKeyguardRootView() } } Loading @@ -67,10 +61,16 @@ interface LockscreenSceneModule { @Provides fun providesLockscreenContent( viewModelFactory: LockscreenContentViewModel.Factory, notificationScrimViewModelFactory: NotificationLockscreenScrimViewModel.Factory, blueprints: Set<@JvmSuppressWildcards ComposableLockscreenSceneBlueprint>, clockInteractor: KeyguardClockInteractor, ): LockscreenContent { return LockscreenContent(viewModelFactory, blueprints, clockInteractor) return LockscreenContent( viewModelFactory, notificationScrimViewModelFactory, blueprints, clockInteractor, ) } } } packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt +14 −5 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.notifications.ui.composable.NotificationLockscreenScrim import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel /** * Renders the content of the lockscreen. Loading @@ -39,6 +41,7 @@ import com.android.systemui.lifecycle.rememberViewModel */ class LockscreenContent( private val viewModelFactory: LockscreenContentViewModel.Factory, private val notificationScrimViewModelFactory: NotificationLockscreenScrimViewModel.Factory, private val blueprints: Set<@JvmSuppressWildcards ComposableLockscreenSceneBlueprint>, private val clockInteractor: KeyguardClockInteractor, ) { Loading @@ -47,10 +50,13 @@ class LockscreenContent( } @Composable fun SceneScope.Content( modifier: Modifier = Modifier, ) { val viewModel = rememberViewModel("LockscreenContent") { viewModelFactory.create() } fun SceneScope.Content(modifier: Modifier = Modifier) { val viewModel = rememberViewModel("LockscreenContent-viewModel") { viewModelFactory.create() } val notificationLockscreenScrimViewModel = rememberViewModel("LockscreenContent-scrimViewModel") { notificationScrimViewModelFactory.create() } val isContentVisible: Boolean by viewModel.isContentVisible.collectAsStateWithLifecycle() if (!isContentVisible) { // If the content isn't supposed to be visible, show a large empty box as it's needed Loading @@ -71,6 +77,9 @@ class LockscreenContent( } val blueprint = blueprintByBlueprintId[blueprintId] ?: return with(blueprint) { Content(viewModel, modifier.sysuiResTag("keyguard_root_view")) } with(blueprint) { Content(viewModel, modifier.sysuiResTag("keyguard_root_view")) NotificationLockscreenScrim(notificationLockscreenScrimViewModel) } } } packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt 0 → 100644 +119 −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.animation.core.Animatable import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.content.state.TransitionState import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel import kotlinx.coroutines.launch /** * A full-screen notifications scrim that is only visible after transitioning from Shade scene to * Lockscreen Scene and ending user input, at which point it fades out, visually completing the * transition. */ @Composable fun SceneScope.NotificationLockscreenScrim( viewModel: NotificationLockscreenScrimViewModel, modifier: Modifier = Modifier, ) { val coroutineScope = rememberCoroutineScope() val shadeMode = viewModel.shadeMode.collectAsStateWithLifecycle() // Important: Make sure that shouldShowScrimFadeOut() is checked the first time the Lockscreen // scene is composed. val useFadeOutOnComposition = remember(shadeMode.value) { layoutState.currentTransition?.let { currentTransition -> shouldShowScrimFadeOut(currentTransition, shadeMode.value) } ?: false } val alphaAnimatable = remember { Animatable(1f) } LaunchedEffect( alphaAnimatable, layoutState.currentTransition, useFadeOutOnComposition, shadeMode, ) { val currentTransition = layoutState.currentTransition if ( useFadeOutOnComposition && currentTransition != null && shouldShowScrimFadeOut(currentTransition, shadeMode.value) && currentTransition.isUserInputOngoing ) { // keep scrim visible until user lifts their finger. viewModel.setAlphaForLockscreenFadeIn(0f) alphaAnimatable.snapTo(1f) } else if ( useFadeOutOnComposition && (currentTransition == null || (shouldShowScrimFadeOut(currentTransition, shadeMode.value) && !currentTransition.isUserInputOngoing)) ) { // we no longer want to keep the scrim from fading out, so animate the scrim fade-out // and pipe the progress to the view model as well, so NSSL can fade-in the stack in // tandem. viewModel.setAlphaForLockscreenFadeIn(0f) coroutineScope.launch { snapshotFlow { alphaAnimatable.value } .collect { viewModel.setAlphaForLockscreenFadeIn(1 - it) } } alphaAnimatable.animateTo(0f, tween()) } else { // disable the scrim fade logic. viewModel.setAlphaForLockscreenFadeIn(1f) alphaAnimatable.snapTo(0f) } } Box( modifier .fillMaxSize() .element(Notifications.Elements.NotificationScrim) .graphicsLayer { alpha = alphaAnimatable.value } .background(MaterialTheme.colorScheme.surface) ) } private fun shouldShowScrimFadeOut( currentTransition: TransitionState.Transition, shadeMode: ShadeMode, ): Boolean { return shadeMode == ShadeMode.Single && currentTransition.isInitiatedByUserInput && (currentTransition.isTransitioning(from = Scenes.Shade, to = Scenes.Lockscreen) || currentTransition.isTransitioning(from = Scenes.Bouncer, to = Scenes.Lockscreen)) } packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +39 −14 Original line number Diff line number Diff line Loading @@ -86,6 +86,8 @@ import com.android.compose.animation.scene.ElementKey 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.SceneTransitionLayoutState import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.modifiers.thenIf import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius import com.android.systemui.res.R Loading @@ -101,7 +103,7 @@ import com.android.systemui.statusbar.notification.stack.ui.viewmodel.Notificati import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch object Notifications { Loading Loading @@ -251,6 +253,7 @@ fun SceneScope.ConstrainedNotificationStack( NotificationPlaceholder( stackScrollView = stackScrollView, viewModel = viewModel, useStackBounds = { shouldUseLockscreenStackBounds(layoutState.transitionState) }, modifier = Modifier.fillMaxSize(), ) HeadsUpNotificationSpace( Loading Loading @@ -363,7 +366,6 @@ fun SceneScope.NotificationScrollingStack( snapshotFlow { syntheticScroll.value } .collect { delta -> scrollNotificationStack( scope = coroutineScope, delta = delta, animate = false, scrimOffset = scrimOffset, Loading @@ -383,7 +385,6 @@ fun SceneScope.NotificationScrollingStack( // composed at least once), and our remote input row overlaps with the ime bounds. if (isRemoteInputActive && imeTopValue > 0f && remoteInputRowBottom > imeTopValue) { scrollNotificationStack( scope = coroutineScope, delta = remoteInputRowBottom - imeTopValue, animate = true, scrimOffset = scrimOffset, Loading Loading @@ -450,7 +451,10 @@ fun SceneScope.NotificationScrollingStack( scrimCornerRadius, screenCornerRadius, { expansionFraction }, shouldAnimateScrimCornerRadius( layoutState, shouldPunchHoleBehindScrim, ), ) .let { scrimRounding.value.toRoundedCornerShape(it) } clip = true Loading Loading @@ -514,6 +518,9 @@ fun SceneScope.NotificationScrollingStack( NotificationPlaceholder( stackScrollView = stackScrollView, viewModel = viewModel, useStackBounds = { !shouldUseLockscreenStackBounds(layoutState.transitionState) }, modifier = Modifier.notificationStackHeight( view = stackScrollView, Loading Loading @@ -600,6 +607,7 @@ fun SceneScope.NotificationStackCutoffGuideline( private fun SceneScope.NotificationPlaceholder( stackScrollView: NotificationScrollView, viewModel: NotificationsPlaceholderViewModel, useStackBounds: () -> Boolean, modifier: Modifier = Modifier, ) { Box( Loading @@ -609,6 +617,12 @@ private fun SceneScope.NotificationPlaceholder( .debugBackground(viewModel, DEBUG_STACK_COLOR) .onSizeChanged { size -> debugLog(viewModel) { "STACK onSizeChanged: size=$size" } } .onGloballyPositioned { coordinates: LayoutCoordinates -> // This element is opted out of the shared element system, so there can be // multiple instances of it during a transition. Thus we need to determine which // instance should feed its bounds to NSSL to avoid providing conflicting values val useBounds = useStackBounds() if (useBounds) { // NOTE: positionInWindow.y scrolls off screen, but boundsInWindow.top won't val positionInWindow = coordinates.positionInWindow() debugLog(viewModel) { "STACK onGloballyPositioned:" + Loading @@ -616,14 +630,13 @@ private fun SceneScope.NotificationPlaceholder( " position=$positionInWindow" + " bounds=${coordinates.boundsInWindow()}" } // NOTE: positionInWindow.y scrolls off screen, but boundsInWindow.top will not stackScrollView.setStackTop(positionInWindow.y) } } ) } private suspend fun scrollNotificationStack( scope: CoroutineScope, delta: Float, animate: Boolean, scrimOffset: Animatable<Float, AnimationVector1D>, Loading @@ -638,7 +651,7 @@ private suspend fun scrollNotificationStack( if (animate) { // launch a new coroutine for the remainder animation so that it doesn't suspend the // scrim animation, allowing both to play simultaneously. scope.launch { scrollState.animateScrollTo(remainingDelta) } coroutineScope { launch { scrollState.animateScrollTo(remainingDelta) } } } else { scrollState.scrollTo(remainingDelta) } Loading @@ -658,6 +671,18 @@ private suspend fun scrollNotificationStack( } } private fun shouldUseLockscreenStackBounds(state: TransitionState): Boolean { return state is TransitionState.Idle && state.currentScene == Scenes.Lockscreen } private fun shouldAnimateScrimCornerRadius( state: SceneTransitionLayoutState, shouldPunchHoleBehindScrim: Boolean, ): Boolean { return shouldPunchHoleBehindScrim || state.isTransitioning(from = Scenes.Shade, to = Scenes.Lockscreen) } private fun calculateCornerRadius( scrimCornerRadius: Dp, screenCornerRadius: Dp, Loading packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt +5 −0 Original line number Diff line number Diff line Loading @@ -82,6 +82,10 @@ val SceneContainerTransitions = transitions { sharedElement(Notifications.Elements.HeadsUpNotificationPlaceholder, enabled = false) } from(Scenes.Shade, to = Scenes.QuickSettings) { shadeToQuickSettingsTransition() } from(Scenes.Shade, to = Scenes.Lockscreen) { reversed { lockscreenToShadeTransition() } sharedElement(Notifications.Elements.NotificationStackPlaceholder, enabled = false) } // Overlay transitions Loading @@ -94,6 +98,7 @@ val SceneContainerTransitions = transitions { // Scene overscroll overscrollDisabled(Scenes.Gone, Orientation.Vertical) overscrollDisabled(Scenes.Lockscreen, Orientation.Vertical) overscroll(Scenes.Bouncer, Orientation.Vertical) { translate(Bouncer.Elements.Content, y = { absoluteDistance }) } Loading Loading
packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt +10 −10 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.keyguard.ui.composable.LockscreenSceneBlueprintModul import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel import com.android.systemui.scene.ui.composable.Scene import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel import dagger.Binds import dagger.Module import dagger.Provides Loading @@ -35,12 +36,7 @@ import dagger.multibindings.IntoSet import javax.inject.Provider import kotlinx.coroutines.ExperimentalCoroutinesApi @Module( includes = [ LockscreenSceneBlueprintModule::class, ], ) @Module(includes = [LockscreenSceneBlueprintModule::class]) interface LockscreenSceneModule { @Binds @IntoSet fun lockscreenScene(scene: LockscreenScene): Scene Loading @@ -51,9 +47,7 @@ interface LockscreenSceneModule { @Provides @SysUISingleton @KeyguardRootView fun viewProvider( configurator: Provider<KeyguardViewConfigurator>, ): () -> View { fun viewProvider(configurator: Provider<KeyguardViewConfigurator>): () -> View { return { configurator.get().getKeyguardRootView() } } Loading @@ -67,10 +61,16 @@ interface LockscreenSceneModule { @Provides fun providesLockscreenContent( viewModelFactory: LockscreenContentViewModel.Factory, notificationScrimViewModelFactory: NotificationLockscreenScrimViewModel.Factory, blueprints: Set<@JvmSuppressWildcards ComposableLockscreenSceneBlueprint>, clockInteractor: KeyguardClockInteractor, ): LockscreenContent { return LockscreenContent(viewModelFactory, blueprints, clockInteractor) return LockscreenContent( viewModelFactory, notificationScrimViewModelFactory, blueprints, clockInteractor, ) } } }
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt +14 −5 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.notifications.ui.composable.NotificationLockscreenScrim import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel /** * Renders the content of the lockscreen. Loading @@ -39,6 +41,7 @@ import com.android.systemui.lifecycle.rememberViewModel */ class LockscreenContent( private val viewModelFactory: LockscreenContentViewModel.Factory, private val notificationScrimViewModelFactory: NotificationLockscreenScrimViewModel.Factory, private val blueprints: Set<@JvmSuppressWildcards ComposableLockscreenSceneBlueprint>, private val clockInteractor: KeyguardClockInteractor, ) { Loading @@ -47,10 +50,13 @@ class LockscreenContent( } @Composable fun SceneScope.Content( modifier: Modifier = Modifier, ) { val viewModel = rememberViewModel("LockscreenContent") { viewModelFactory.create() } fun SceneScope.Content(modifier: Modifier = Modifier) { val viewModel = rememberViewModel("LockscreenContent-viewModel") { viewModelFactory.create() } val notificationLockscreenScrimViewModel = rememberViewModel("LockscreenContent-scrimViewModel") { notificationScrimViewModelFactory.create() } val isContentVisible: Boolean by viewModel.isContentVisible.collectAsStateWithLifecycle() if (!isContentVisible) { // If the content isn't supposed to be visible, show a large empty box as it's needed Loading @@ -71,6 +77,9 @@ class LockscreenContent( } val blueprint = blueprintByBlueprintId[blueprintId] ?: return with(blueprint) { Content(viewModel, modifier.sysuiResTag("keyguard_root_view")) } with(blueprint) { Content(viewModel, modifier.sysuiResTag("keyguard_root_view")) NotificationLockscreenScrim(notificationLockscreenScrimViewModel) } } }
packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt 0 → 100644 +119 −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.animation.core.Animatable import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.content.state.TransitionState import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel import kotlinx.coroutines.launch /** * A full-screen notifications scrim that is only visible after transitioning from Shade scene to * Lockscreen Scene and ending user input, at which point it fades out, visually completing the * transition. */ @Composable fun SceneScope.NotificationLockscreenScrim( viewModel: NotificationLockscreenScrimViewModel, modifier: Modifier = Modifier, ) { val coroutineScope = rememberCoroutineScope() val shadeMode = viewModel.shadeMode.collectAsStateWithLifecycle() // Important: Make sure that shouldShowScrimFadeOut() is checked the first time the Lockscreen // scene is composed. val useFadeOutOnComposition = remember(shadeMode.value) { layoutState.currentTransition?.let { currentTransition -> shouldShowScrimFadeOut(currentTransition, shadeMode.value) } ?: false } val alphaAnimatable = remember { Animatable(1f) } LaunchedEffect( alphaAnimatable, layoutState.currentTransition, useFadeOutOnComposition, shadeMode, ) { val currentTransition = layoutState.currentTransition if ( useFadeOutOnComposition && currentTransition != null && shouldShowScrimFadeOut(currentTransition, shadeMode.value) && currentTransition.isUserInputOngoing ) { // keep scrim visible until user lifts their finger. viewModel.setAlphaForLockscreenFadeIn(0f) alphaAnimatable.snapTo(1f) } else if ( useFadeOutOnComposition && (currentTransition == null || (shouldShowScrimFadeOut(currentTransition, shadeMode.value) && !currentTransition.isUserInputOngoing)) ) { // we no longer want to keep the scrim from fading out, so animate the scrim fade-out // and pipe the progress to the view model as well, so NSSL can fade-in the stack in // tandem. viewModel.setAlphaForLockscreenFadeIn(0f) coroutineScope.launch { snapshotFlow { alphaAnimatable.value } .collect { viewModel.setAlphaForLockscreenFadeIn(1 - it) } } alphaAnimatable.animateTo(0f, tween()) } else { // disable the scrim fade logic. viewModel.setAlphaForLockscreenFadeIn(1f) alphaAnimatable.snapTo(0f) } } Box( modifier .fillMaxSize() .element(Notifications.Elements.NotificationScrim) .graphicsLayer { alpha = alphaAnimatable.value } .background(MaterialTheme.colorScheme.surface) ) } private fun shouldShowScrimFadeOut( currentTransition: TransitionState.Transition, shadeMode: ShadeMode, ): Boolean { return shadeMode == ShadeMode.Single && currentTransition.isInitiatedByUserInput && (currentTransition.isTransitioning(from = Scenes.Shade, to = Scenes.Lockscreen) || currentTransition.isTransitioning(from = Scenes.Bouncer, to = Scenes.Lockscreen)) }
packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +39 −14 Original line number Diff line number Diff line Loading @@ -86,6 +86,8 @@ import com.android.compose.animation.scene.ElementKey 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.SceneTransitionLayoutState import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.modifiers.thenIf import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius import com.android.systemui.res.R Loading @@ -101,7 +103,7 @@ import com.android.systemui.statusbar.notification.stack.ui.viewmodel.Notificati import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch object Notifications { Loading Loading @@ -251,6 +253,7 @@ fun SceneScope.ConstrainedNotificationStack( NotificationPlaceholder( stackScrollView = stackScrollView, viewModel = viewModel, useStackBounds = { shouldUseLockscreenStackBounds(layoutState.transitionState) }, modifier = Modifier.fillMaxSize(), ) HeadsUpNotificationSpace( Loading Loading @@ -363,7 +366,6 @@ fun SceneScope.NotificationScrollingStack( snapshotFlow { syntheticScroll.value } .collect { delta -> scrollNotificationStack( scope = coroutineScope, delta = delta, animate = false, scrimOffset = scrimOffset, Loading @@ -383,7 +385,6 @@ fun SceneScope.NotificationScrollingStack( // composed at least once), and our remote input row overlaps with the ime bounds. if (isRemoteInputActive && imeTopValue > 0f && remoteInputRowBottom > imeTopValue) { scrollNotificationStack( scope = coroutineScope, delta = remoteInputRowBottom - imeTopValue, animate = true, scrimOffset = scrimOffset, Loading Loading @@ -450,7 +451,10 @@ fun SceneScope.NotificationScrollingStack( scrimCornerRadius, screenCornerRadius, { expansionFraction }, shouldAnimateScrimCornerRadius( layoutState, shouldPunchHoleBehindScrim, ), ) .let { scrimRounding.value.toRoundedCornerShape(it) } clip = true Loading Loading @@ -514,6 +518,9 @@ fun SceneScope.NotificationScrollingStack( NotificationPlaceholder( stackScrollView = stackScrollView, viewModel = viewModel, useStackBounds = { !shouldUseLockscreenStackBounds(layoutState.transitionState) }, modifier = Modifier.notificationStackHeight( view = stackScrollView, Loading Loading @@ -600,6 +607,7 @@ fun SceneScope.NotificationStackCutoffGuideline( private fun SceneScope.NotificationPlaceholder( stackScrollView: NotificationScrollView, viewModel: NotificationsPlaceholderViewModel, useStackBounds: () -> Boolean, modifier: Modifier = Modifier, ) { Box( Loading @@ -609,6 +617,12 @@ private fun SceneScope.NotificationPlaceholder( .debugBackground(viewModel, DEBUG_STACK_COLOR) .onSizeChanged { size -> debugLog(viewModel) { "STACK onSizeChanged: size=$size" } } .onGloballyPositioned { coordinates: LayoutCoordinates -> // This element is opted out of the shared element system, so there can be // multiple instances of it during a transition. Thus we need to determine which // instance should feed its bounds to NSSL to avoid providing conflicting values val useBounds = useStackBounds() if (useBounds) { // NOTE: positionInWindow.y scrolls off screen, but boundsInWindow.top won't val positionInWindow = coordinates.positionInWindow() debugLog(viewModel) { "STACK onGloballyPositioned:" + Loading @@ -616,14 +630,13 @@ private fun SceneScope.NotificationPlaceholder( " position=$positionInWindow" + " bounds=${coordinates.boundsInWindow()}" } // NOTE: positionInWindow.y scrolls off screen, but boundsInWindow.top will not stackScrollView.setStackTop(positionInWindow.y) } } ) } private suspend fun scrollNotificationStack( scope: CoroutineScope, delta: Float, animate: Boolean, scrimOffset: Animatable<Float, AnimationVector1D>, Loading @@ -638,7 +651,7 @@ private suspend fun scrollNotificationStack( if (animate) { // launch a new coroutine for the remainder animation so that it doesn't suspend the // scrim animation, allowing both to play simultaneously. scope.launch { scrollState.animateScrollTo(remainingDelta) } coroutineScope { launch { scrollState.animateScrollTo(remainingDelta) } } } else { scrollState.scrollTo(remainingDelta) } Loading @@ -658,6 +671,18 @@ private suspend fun scrollNotificationStack( } } private fun shouldUseLockscreenStackBounds(state: TransitionState): Boolean { return state is TransitionState.Idle && state.currentScene == Scenes.Lockscreen } private fun shouldAnimateScrimCornerRadius( state: SceneTransitionLayoutState, shouldPunchHoleBehindScrim: Boolean, ): Boolean { return shouldPunchHoleBehindScrim || state.isTransitioning(from = Scenes.Shade, to = Scenes.Lockscreen) } private fun calculateCornerRadius( scrimCornerRadius: Dp, screenCornerRadius: Dp, Loading
packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt +5 −0 Original line number Diff line number Diff line Loading @@ -82,6 +82,10 @@ val SceneContainerTransitions = transitions { sharedElement(Notifications.Elements.HeadsUpNotificationPlaceholder, enabled = false) } from(Scenes.Shade, to = Scenes.QuickSettings) { shadeToQuickSettingsTransition() } from(Scenes.Shade, to = Scenes.Lockscreen) { reversed { lockscreenToShadeTransition() } sharedElement(Notifications.Elements.NotificationStackPlaceholder, enabled = false) } // Overlay transitions Loading @@ -94,6 +98,7 @@ val SceneContainerTransitions = transitions { // Scene overscroll overscrollDisabled(Scenes.Gone, Orientation.Vertical) overscrollDisabled(Scenes.Lockscreen, Orientation.Vertical) overscroll(Scenes.Bouncer, Orientation.Vertical) { translate(Bouncer.Elements.Content, y = { absoluteDistance }) } Loading