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

Commit 7d1ee563 authored by Danny Burakov's avatar Danny Burakov
Browse files

[flexiglass] Consolidate drawBounds updates by HUN space into one place.

Fix: 441922785
Bug: 440059830
Test: Manually tested by receiving HUNs with NSSL debug lines showing
 the updated draw bounds.
Flag: com.android.systemui.scene_container
Change-Id: I4eead1a72f97558a39afb6838fb56b2d9d0cf3c0
parent 8a263bad
Loading
Loading
Loading
Loading
+0 −9
Original line number Original line Diff line number Diff line
@@ -28,7 +28,6 @@ import com.android.compose.animation.scene.ElementContentScope
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.composable.ConstrainedNotificationStack
import com.android.systemui.notifications.ui.composable.ConstrainedNotificationStack
import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElement
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElement
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementContext
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementContext
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementFactory
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementFactory
@@ -98,12 +97,4 @@ constructor(
            modifier = modifier.fillMaxSize(),
            modifier = modifier.fillMaxSize(),
        )
        )
    }
    }

    @Composable
    private fun ContentScope.HeadsUpNotifications(modifier: Modifier = Modifier) {
        SnoozeableHeadsUpNotificationSpace(
            stackScrollView = stackScrollView.get(),
            viewModel = rememberViewModel("HeadsUpNotifications") { viewModelFactory.create() },
        )
    }
}
}
+38 −4
Original line number Original line Diff line number Diff line
@@ -68,6 +68,7 @@ import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.toAndroidRectF
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -181,14 +182,18 @@ fun ContentScope.HeadsUpNotificationSpace(
/**
/**
 * A version of [HeadsUpNotificationSpace] that can be swiped up off the top edge of the screen by
 * A version of [HeadsUpNotificationSpace] that can be swiped up off the top edge of the screen by
 * the user. When swiped up, the heads up notification is snoozed.
 * the user. When swiped up, the heads up notification is snoozed.
 *
 * @param useDrawBounds Whether to communicate drawBounds updated to the [stackScrollView]. This
 *   should be `true` when content rendering the regular stack is not setting draw bounds anymore,
 *   but HUNs can still appear.
 */
 */
@Composable
@Composable
fun ContentScope.SnoozeableHeadsUpNotificationSpace(
fun ContentScope.SnoozeableHeadsUpNotificationSpace(
    useDrawBounds: () -> Boolean,
    stackScrollView: NotificationScrollView,
    stackScrollView: NotificationScrollView,
    viewModel: NotificationsPlaceholderViewModel,
    viewModel: NotificationsPlaceholderViewModel,
    modifier: Modifier = Modifier,
    modifier: Modifier = Modifier,
) {
) {

    val isSnoozable by viewModel.isHeadsUpOrAnimatingAway.collectAsStateWithLifecycle(false)
    val isSnoozable by viewModel.isHeadsUpOrAnimatingAway.collectAsStateWithLifecycle(false)


    var scrollOffset by remember { mutableFloatStateOf(0f) }
    var scrollOffset by remember { mutableFloatStateOf(0f) }
@@ -229,11 +234,38 @@ fun ContentScope.SnoozeableHeadsUpNotificationSpace(
        }
        }
    }
    }


    // Wait for being Idle on this content, otherwise LaunchedEffect would fire too soon, and
    // another transition could override the NSSL stack bounds.
    val updateDrawBounds = layoutState.transitionState.isIdle() && useDrawBounds()

    LaunchedEffect(updateDrawBounds) {
        if (updateDrawBounds) {
            // Reset the stack bounds to avoid caching these values from the previous Scenes, and
            // not to confuse the StackScrollAlgorithm when it displays a HUN over GONE.
            stackScrollView.apply {
                // use -headsUpInset to allow HUN translation outside bounds for snoozing
                setStackTop(-headsUpInset)
            }
        }
    }

    HeadsUpNotificationSpace(
    HeadsUpNotificationSpace(
        stackScrollView = stackScrollView,
        stackScrollView = stackScrollView,
        viewModel = viewModel,
        viewModel = viewModel,
        modifier =
        modifier =
            modifier
            modifier
                .onGloballyPositioned {
                    if (updateDrawBounds) {
                        stackScrollView.updateDrawBounds(
                            it.boundsInWindow().toAndroidRectF().apply {
                                // extend bounds to the screen top to avoid cutting off HUN
                                // transitions
                                top = 0f
                                bottom += headsUpInset
                            }
                        )
                    }
                }
                .absoluteOffset {
                .absoluteOffset {
                    IntOffset(
                    IntOffset(
                        x = 0,
                        x = 0,
@@ -836,13 +868,15 @@ private suspend fun scrollNotificationStack(
private fun TransitionState.isOnLockscreen(): Boolean {
private fun TransitionState.isOnLockscreen(): Boolean {
    return currentScene == Scenes.Lockscreen && currentOverlays.isEmpty()
    return currentScene == Scenes.Lockscreen && currentOverlays.isEmpty()
}
}

private fun shouldUseLockscreenStackBounds(state: TransitionState): Boolean {
private fun shouldUseLockscreenStackBounds(state: TransitionState): Boolean {
    return when (state) {
    return when (state) {
        is TransitionState.Idle -> state.isOnLockscreen()
        is TransitionState.Idle -> state.isOnLockscreen()
        is TransitionState.Transition ->
        is TransitionState.Transition ->
            // Keep using the lockscreen stack bounds when there is no placeholder on the next content
            // Keep using the lockscreen stack bounds when there is no placeholder on the next
            state.fromContent == Scenes.Lockscreen && state.toContent != Scenes.Shade
            // content
                || state.isTransitioningBetween(content = Scenes.Lockscreen, other = Overlays.Bouncer)
            state.fromContent == Scenes.Lockscreen && state.toContent != Scenes.Shade ||
                state.isTransitioningBetween(content = Scenes.Lockscreen, other = Overlays.Bouncer)
    }
    }
}
}


+8 −46
Original line number Original line Diff line number Diff line
@@ -19,13 +19,8 @@ package com.android.systemui.occluded.ui.composable
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toAndroidRectF
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult
@@ -34,7 +29,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
import com.android.systemui.notifications.ui.composable.headsUpTopInset
import com.android.systemui.qs.shared.ui.QuickSettings
import com.android.systemui.qs.shared.ui.QuickSettings
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.Scenes
@@ -46,7 +40,7 @@ import dagger.Lazy
import javax.inject.Inject
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.Flow


/** The occluded scene shows when a non-dream activity is showing over keyguard */
/** The occluded scene shows when a non-dream activity is showing over the lock screen. */
@SysUISingleton
@SysUISingleton
class OccludedScene
class OccludedScene
@Inject
@Inject
@@ -69,49 +63,17 @@ constructor(


    @Composable
    @Composable
    override fun ContentScope.Content(modifier: Modifier) {
    override fun ContentScope.Content(modifier: Modifier) {

        val isIdleAndNotShadeExpanded =
            with(layoutState.transitionState) {
                isIdle(key) &&
                    !isIdle(Overlays.NotificationsShade) &&
                    !isIdle(Overlays.QuickSettingsShade)
            }

        val headsUpInset = with(LocalDensity.current) { headsUpTopInset().toPx() }

        LaunchedEffect(isIdleAndNotShadeExpanded) {
            // Wait for being Idle on this Scene, otherwise LaunchedEffect would fire too soon,
            // and another transition could override the NSSL stack bounds.
            if (isIdleAndNotShadeExpanded) {
                // Reset the stack bounds to avoid caching these values from the previous Scenes,
                // and not to confuse the StackScrollAlgorithm when it displays a HUN over OCCLUDED.
                notificationStackScrollView.get().apply {
                    // use -headsUpInset to allow HUN translation outside bounds for snoozing
                    setStackTop(-headsUpInset)
                }
            }
        }

        animateContentFloatAsState(
        animateContentFloatAsState(
            value = QuickSettings.SharedValues.SquishinessValues.OccludedSceneStarting,
            value = QuickSettings.SharedValues.SquishinessValues.OccludedSceneStarting,
            key = QuickSettings.SharedValues.TilesSquishiness,
            key = QuickSettings.SharedValues.TilesSquishiness,
        )
        )
        Spacer(modifier.fillMaxSize())
        Spacer(modifier.fillMaxSize())
        SnoozeableHeadsUpNotificationSpace(
        SnoozeableHeadsUpNotificationSpace(
            modifier =
            useDrawBounds = {
                Modifier.onGloballyPositioned {
                with(layoutState.transitionState) {
                    // Once we are on the non-occluded Lockscreen, the regular stack is not setting
                    isIdle(key) &&
                    // draw bounds anymore, but HUNs can still appear.
                        !isIdle(Overlays.NotificationsShade) &&
                    if (isIdleAndNotShadeExpanded) {
                        !isIdle(Overlays.QuickSettingsShade)
                        notificationStackScrollView
                            .get()
                            .updateDrawBounds(
                                it.boundsInWindow().toAndroidRectF().apply {
                                    // extend bounds to the screen top to avoid cutting off HUN
                                    // transitions
                                    top = 0f
                                }
                            )
                }
                }
            },
            },
            stackScrollView = notificationStackScrollView.get(),
            stackScrollView = notificationStackScrollView.get(),
+8 −0
Original line number Original line Diff line number Diff line
@@ -92,6 +92,7 @@ import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayActionsView
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayContentViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayContentViewModel
import com.android.systemui.res.R
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.Overlay
import com.android.systemui.scene.ui.composable.Overlay
import com.android.systemui.shade.ui.composable.ChipHighlightModel
import com.android.systemui.shade.ui.composable.ChipHighlightModel
import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.shade.ui.composable.OverlayShade
@@ -218,6 +219,13 @@ constructor(
                )
                )
            }
            }
            SnoozeableHeadsUpNotificationSpace(
            SnoozeableHeadsUpNotificationSpace(
                useDrawBounds = {
                    with(layoutState.transitionState) {
                        // When overlaid on top of the lock screen, drawBounds updates are already
                        // being sent.
                        isIdle(key) && !isIdle(Scenes.Lockscreen)
                    }
                },
                stackScrollView = notificationStackScrollView.get(),
                stackScrollView = notificationStackScrollView.get(),
                viewModel = hunPlaceholderViewModel,
                viewModel = hunPlaceholderViewModel,
            )
            )
+10 −50
Original line number Original line Diff line number Diff line
@@ -19,13 +19,7 @@ package com.android.systemui.scene.ui.composable
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toAndroidRectF
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult
@@ -34,7 +28,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
import com.android.systemui.notifications.ui.composable.headsUpTopInset
import com.android.systemui.qs.shared.ui.QuickSettings
import com.android.systemui.qs.shared.ui.QuickSettings
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.Scenes
@@ -47,13 +40,13 @@ import kotlinx.coroutines.flow.Flow


/**
/**
 * "Gone" is not a real scene but rather the absence of scenes when we want to skip showing any
 * "Gone" is not a real scene but rather the absence of scenes when we want to skip showing any
 * content from the scene framework.
 * scene from the scene framework. Overlays from the scene framework may still be shown.
 */
 */
@SysUISingleton
@SysUISingleton
class GoneScene
class GoneScene
@Inject
@Inject
constructor(
constructor(
    private val notificationStackScrolLView: Lazy<NotificationScrollView>,
    private val notificationStackScrollView: Lazy<NotificationScrollView>,
    private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
    private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
    private val viewModelFactory: GoneUserActionsViewModel.Factory,
    private val viewModelFactory: GoneUserActionsViewModel.Factory,
) : ExclusiveActivatable(), Scene {
) : ExclusiveActivatable(), Scene {
@@ -71,53 +64,20 @@ constructor(


    @Composable
    @Composable
    override fun ContentScope.Content(modifier: Modifier) {
    override fun ContentScope.Content(modifier: Modifier) {

        val isIdleAndNotOccluded =
            with(layoutState.transitionState) {
                isIdle(key) &&
                    !isIdle(Overlays.NotificationsShade) &&
                    !isIdle(Overlays.QuickSettingsShade)
            }

        val headsUpInset = with(LocalDensity.current) { headsUpTopInset().toPx() }

        LaunchedEffect(isIdleAndNotOccluded) {
            // Wait for being Idle on this Scene, otherwise LaunchedEffect would fire too soon,
            // and another transition could override the NSSL stack bounds.
            if (isIdleAndNotOccluded) {
                // Reset the stack bounds to avoid caching these values from the previous Scenes,
                // and not to confuse the StackScrollAlgorithm when it displays a HUN over GONE.
                notificationStackScrolLView.get().apply {
                    // use -headsUpInset to allow HUN translation outside bounds for snoozing
                    setStackTop(-headsUpInset)
                }
            }
        }

        animateContentFloatAsState(
        animateContentFloatAsState(
            value = QuickSettings.SharedValues.SquishinessValues.GoneSceneStarting,
            value = QuickSettings.SharedValues.SquishinessValues.GoneSceneStarting,
            key = QuickSettings.SharedValues.TilesSquishiness,
            key = QuickSettings.SharedValues.TilesSquishiness,
        )
        )
        Spacer(modifier.fillMaxSize())
        Spacer(modifier.fillMaxSize())
        SnoozeableHeadsUpNotificationSpace(
        SnoozeableHeadsUpNotificationSpace(
            modifier =
            useDrawBounds = {
                Modifier.onGloballyPositioned {
                with(layoutState.transitionState) {
                    // Once we are on the non-occluded Lockscreen, the regular stack is not setting
                    isIdle(key) &&
                    // draw bounds anymore, but HUNs can still appear.
                        !isIdle(Overlays.NotificationsShade) &&
                    if (isIdleAndNotOccluded) {
                        !isIdle(Overlays.QuickSettingsShade)
                        notificationStackScrolLView
                            .get()
                            .updateDrawBounds(
                                it.boundsInWindow().toAndroidRectF().apply {
                                    // extend bounds to the screen top to avoid cutting off HUN
                                    // transitions
                                    top = 0f
                                    bottom += headsUpInset
                                }
                            )
                }
                }
            },
            },
            stackScrollView = notificationStackScrolLView.get(),
            stackScrollView = notificationStackScrollView.get(),
            viewModel =
            viewModel =
                rememberViewModel("GoneScene") { notificationsPlaceholderViewModelFactory.create() },
                rememberViewModel("GoneScene") { notificationsPlaceholderViewModelFactory.create() },
        )
        )