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

Commit b293baec authored by Shawn Lee's avatar Shawn Lee Committed by Android (Google) Code Review
Browse files

Merge "[flexiglass] Clean up Gone -> Shade -> QS transitions" into main

parents e874af5b c7948012
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -30,12 +30,13 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.LifecycleOwner
import com.android.compose.theme.PlatformTheme
import com.android.compose.ui.platform.DensityAwareComposeView
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.composable.BouncerContent
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutoutProvider
import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
import com.android.systemui.communal.ui.compose.CommunalContainer
import com.android.systemui.communal.ui.compose.CommunalHub
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
@@ -129,8 +130,9 @@ object ComposeFacade : BaseComposeFacade {
        return ComposeView(context).apply {
            setContent {
                PlatformTheme {
                    DisplayCutoutProvider(
                        displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets)
                    ScreenDecorProvider(
                        displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets),
                        screenCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
                    ) {
                        SceneContainer(
                            viewModel = viewModel,
+14 −3
Original line number Diff line number Diff line
@@ -21,17 +21,28 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.StateFlow

/** The bounds and [CutoutLocation] of the current display. */
val LocalDisplayCutout = staticCompositionLocalOf { DisplayCutout() }

/** The corner radius in px of the current display. */
val LocalScreenCornerRadius = staticCompositionLocalOf { 0.dp }

@Composable
fun DisplayCutoutProvider(
fun ScreenDecorProvider(
    displayCutout: StateFlow<DisplayCutout>,
    screenCornerRadius: Float,
    content: @Composable () -> Unit,
) {
    val cutout by displayCutout.collectAsState()

    CompositionLocalProvider(LocalDisplayCutout provides cutout) { content() }
    val screenCornerRadiusDp = with(LocalDensity.current) { screenCornerRadius.toDp() }
    CompositionLocalProvider(
        LocalScreenCornerRadius provides screenCornerRadiusDp,
        LocalDisplayCutout provides cutout
    ) {
        content()
    }
}
+79 −21
Original line number Diff line number Diff line
@@ -51,11 +51,13 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
@@ -63,10 +65,15 @@ import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.NestedScrollBehavior
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.height
import com.android.compose.ui.util.lerp
import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
import com.android.systemui.notifications.ui.composable.Notifications.Form
import com.android.systemui.notifications.ui.composable.Notifications.TransitionThresholds.EXPANSION_FOR_MAX_CORNER_RADIUS
import com.android.systemui.notifications.ui.composable.Notifications.TransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA
import com.android.systemui.scene.ui.composable.Gone
import com.android.systemui.scene.ui.composable.Shade
import com.android.systemui.shade.ui.composable.ShadeHeader
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationStackAppearanceViewBinder.SCRIM_CORNER_RADIUS
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import kotlin.math.roundToInt

@@ -77,6 +84,13 @@ object Notifications {
        val ShelfSpace = ElementKey("ShelfSpace")
    }

    // Expansion fraction thresholds (between 0-1f) at which the corresponding value should be
    // at its maximum, given they are at their minimum value at expansion = 0f.
    object TransitionThresholds {
        const val EXPANSION_FOR_MAX_CORNER_RADIUS = 0.1f
        const val EXPANSION_FOR_MAX_SCRIM_ALPHA = 0.3f
    }

    enum class Form {
        HunFromTop,
        Stack,
@@ -125,19 +139,19 @@ fun SceneScope.NotificationScrollingStack(
    modifier: Modifier = Modifier,
) {
    val density = LocalDensity.current
    val cornerRadius by viewModel.cornerRadiusDp.collectAsState()
    val screenCornerRadius = LocalScreenCornerRadius.current
    val expansionFraction by viewModel.expandFraction.collectAsState(0f)

    val navBarHeight =
        with(density) { WindowInsets.systemBars.asPaddingValues().calculateBottomPadding().toPx() }
    val statusBarHeight =
        with(density) { WindowInsets.systemBars.asPaddingValues().calculateTopPadding().toPx() }
    val displayCutoutHeight =
        with(density) { WindowInsets.displayCutout.asPaddingValues().calculateTopPadding().toPx() }
    val statusBarHeight = WindowInsets.systemBars.asPaddingValues().calculateTopPadding()
    val displayCutoutHeight = WindowInsets.displayCutout.asPaddingValues().calculateTopPadding()
    val screenHeight =
        with(density) { LocalConfiguration.current.screenHeightDp.dp.toPx() } +
            navBarHeight +
            maxOf(statusBarHeight, displayCutoutHeight)
        with(density) {
            (LocalConfiguration.current.screenHeightDp.dp +
                    maxOf(statusBarHeight, displayCutoutHeight))
                .toPx()
        } + navBarHeight

    val contentHeight = viewModel.intrinsicContentHeight.collectAsState()

@@ -171,26 +185,53 @@ fun SceneScope.NotificationScrollingStack(
            .collect { shouldCollapse -> if (shouldCollapse) scrimOffset.value = 0f }
    }

    Box(modifier = modifier.element(Notifications.Elements.NotificationScrim)) {
        Spacer(
    Box(
        modifier =
                Modifier.fillMaxSize()
            modifier
                .element(Notifications.Elements.NotificationScrim)
                .offset {
                    // if scrim is expanded while transitioning to Gone scene, increase the offset
                    // in step with the transition so that it is 0 when it completes.
                    if (
                        scrimOffset.value < 0 &&
                            layoutState.isTransitioning(from = Shade, to = Gone)
                    ) {
                        IntOffset(x = 0, y = (scrimOffset.value * expansionFraction).roundToInt())
                    } else {
                        IntOffset(x = 0, y = scrimOffset.value.roundToInt())
                    }
                }
                .graphicsLayer {
                        shape = RoundedCornerShape(cornerRadius.dp)
                    shape =
                        calculateCornerRadius(
                                screenCornerRadius,
                                { expansionFraction },
                                layoutState.isTransitioningBetween(Gone, Shade)
                            )
                            .let {
                                RoundedCornerShape(
                                    topStart = it,
                                    topEnd = it,
                                )
                            }
                    clip = true
                }
                    .drawBehind { drawRect(Color.Black, blendMode = BlendMode.DstOut) }
    ) {
        // Creates a cutout in the background scrim in the shape of the notifications scrim.
        // Only visible when notif scrim alpha < 1, during shade expansion.
        Spacer(
            modifier =
                Modifier.fillMaxSize().drawBehind {
                    drawRect(Color.Black, blendMode = BlendMode.DstOut)
                }
        )
        Box(
            modifier =
                Modifier.fillMaxSize()
                    .offset { IntOffset(0, scrimOffset.value.roundToInt()) }
                    .graphicsLayer {
                        shape = RoundedCornerShape(cornerRadius.dp)
                        clip = true
                        alpha =
                            if (layoutState.isTransitioningBetween(Gone, Shade)) {
                                (expansionFraction / 0.3f).coerceAtMost(1f)
                                (expansionFraction / EXPANSION_FOR_MAX_SCRIM_ALPHA).coerceAtMost(1f)
                            } else 1f
                    }
                    .background(MaterialTheme.colorScheme.surface)
@@ -278,10 +319,10 @@ private fun SceneScope.NotificationPlaceholder(
                .onSizeChanged { size: IntSize ->
                    debugLog(viewModel) { "STACK onSizeChanged: size=$size" }
                }
                .onPlaced { coordinates: LayoutCoordinates ->
                .onGloballyPositioned { coordinates: LayoutCoordinates ->
                    viewModel.onContentTopChanged(coordinates.positionInWindow().y)
                    debugLog(viewModel) {
                        "STACK onPlaced:" +
                        "STACK onGloballyPositioned:" +
                            " size=${coordinates.size}" +
                            " position=${coordinates.positionInWindow()}" +
                            " bounds=${coordinates.boundsInWindow()}"
@@ -310,6 +351,23 @@ private fun SceneScope.NotificationPlaceholder(
    }
}

private fun calculateCornerRadius(
    screenCornerRadius: Dp,
    expansionFraction: () -> Float,
    transitioning: Boolean,
): Dp {
    return if (transitioning) {
        lerp(
                start = screenCornerRadius.value,
                stop = SCRIM_CORNER_RADIUS,
                fraction = (expansionFraction() / EXPANSION_FOR_MAX_CORNER_RADIUS).coerceAtMost(1f),
            )
            .dp
    } else {
        SCRIM_CORNER_RADIUS.dp
    }
}

private inline fun debugLog(
    viewModel: NotificationsPlaceholderViewModel,
    msg: () -> Any,
+2 −38
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
@@ -47,11 +46,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.unit.dp
@@ -62,7 +56,6 @@ import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.qs.footer.ui.compose.FooterActions
import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel
import com.android.systemui.scene.shared.model.SceneKey
@@ -122,8 +115,6 @@ private fun SceneScope.QuickSettingsScene(
    statusBarIconController: StatusBarIconController,
    modifier: Modifier = Modifier,
) {
    val cornerRadius by viewModel.notifications.cornerRadiusDp.collectAsState()

    // TODO(b/280887232): implement the real UI.
    Box(modifier = modifier.fillMaxSize()) {
        val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState()
@@ -155,9 +146,9 @@ private fun SceneScope.QuickSettingsScene(
        // a background that extends to the edges.
        Spacer(
            modifier =
                Modifier.element(Shade.Elements.ScrimBackground)
                Modifier.element(Shade.Elements.BackgroundScrim)
                    .fillMaxSize()
                    .background(MaterialTheme.colorScheme.scrim, shape = Shade.Shapes.Scrim)
                    .background(MaterialTheme.colorScheme.scrim)
        )
        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
@@ -242,32 +233,5 @@ private fun SceneScope.QuickSettingsScene(
                }
            }
        }
        // Scrim with height 0 aligned to bottom of the screen to facilitate shared element
        // transition from Shade scene.
        Box(
            modifier =
                Modifier.element(Notifications.Elements.NotificationScrim)
                    .fillMaxWidth()
                    .height(0.dp)
                    .graphicsLayer {
                        shape = RoundedCornerShape(cornerRadius.dp)
                        clip = true
                        alpha = 1f
                    }
                    .background(MaterialTheme.colorScheme.surface)
                    .align(Alignment.BottomCenter)
                    .onPlaced { coordinates: LayoutCoordinates ->
                        viewModel.notifications.onContentTopChanged(
                            coordinates.positionInWindow().y
                        )
                        val boundsInWindow = coordinates.boundsInWindow()
                        viewModel.notifications.onBoundsChanged(
                            left = boundsInWindow.left,
                            top = boundsInWindow.top,
                            right = boundsInWindow.right,
                            bottom = boundsInWindow.bottom,
                        )
                    }
        )
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -16,18 +16,18 @@

package com.android.systemui.scene.ui.composable

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.Edge
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.shade.ui.composable.Shade
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
@@ -63,6 +63,6 @@ constructor(
    override fun SceneScope.Content(
        modifier: Modifier,
    ) {
        Box(modifier = Modifier.fillMaxSize().element(Notifications.Elements.NotificationScrim))
        Spacer(modifier.fillMaxSize())
    }
}
Loading