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

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

Merge "[Flexiglass] Convert Bouncer scene into an overlay" into main

parents 9cc46244 eff5e9a1
Loading
Loading
Loading
Loading
+5 −6
Original line number Diff line number Diff line
/*
 * Copyright 2023 The Android Open Source Project
 * Copyright (C) 2025 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.
@@ -16,14 +16,13 @@

package com.android.systemui.scene

import com.android.systemui.bouncer.ui.composable.BouncerScene
import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.bouncer.ui.composable.BouncerOverlay
import com.android.systemui.scene.ui.composable.Overlay
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet

@Module
interface BouncerSceneModule {

    @Binds @IntoSet fun bouncerScene(scene: BouncerScene): Scene
interface BouncerOverlayModule {
    @Binds @IntoSet fun bouncer(overlay: BouncerOverlay): Overlay
}
+23 −19
Original line number Diff line number Diff line
@@ -98,7 +98,7 @@ import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerMessageViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerOverlayContentViewModel
import com.android.systemui.bouncer.ui.viewmodel.MessageViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
@@ -118,7 +118,7 @@ import platform.test.motion.compose.values.motionTestValues

@Composable
fun BouncerContent(
    viewModel: BouncerSceneContentViewModel,
    viewModel: BouncerOverlayContentViewModel,
    dialogFactory: BouncerDialogFactory,
    modifier: Modifier = Modifier,
) {
@@ -131,8 +131,8 @@ fun BouncerContent(
@Composable
@VisibleForTesting
fun BouncerContent(
    layout: BouncerSceneLayout,
    viewModel: BouncerSceneContentViewModel,
    layout: BouncerOverlayLayout,
    viewModel: BouncerOverlayContentViewModel,
    dialogFactory: BouncerDialogFactory,
    modifier: Modifier,
) {
@@ -147,11 +147,12 @@ fun BouncerContent(
        modifier = modifier.imePadding().onKeyEvent(viewModel::onKeyEvent).scale(scale)
    ) {
        when (layout) {
            BouncerSceneLayout.STANDARD_BOUNCER -> StandardLayout(viewModel = viewModel)
            BouncerSceneLayout.BESIDE_USER_SWITCHER ->
            BouncerOverlayLayout.STANDARD_BOUNCER -> StandardLayout(viewModel = viewModel)
            BouncerOverlayLayout.BESIDE_USER_SWITCHER ->
                BesideUserSwitcherLayout(viewModel = viewModel)
            BouncerSceneLayout.BELOW_USER_SWITCHER -> BelowUserSwitcherLayout(viewModel = viewModel)
            BouncerSceneLayout.SPLIT_BOUNCER -> SplitLayout(viewModel = viewModel)
            BouncerOverlayLayout.BELOW_USER_SWITCHER ->
                BelowUserSwitcherLayout(viewModel = viewModel)
            BouncerOverlayLayout.SPLIT_BOUNCER -> SplitLayout(viewModel = viewModel)
        }

        Dialog(bouncerViewModel = viewModel, dialogFactory = dialogFactory)
@@ -163,7 +164,10 @@ fun BouncerContent(
 * authentication attempt, including all messaging UI (directives, reasoning, errors, etc.).
 */
@Composable
private fun StandardLayout(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) {
private fun StandardLayout(
    viewModel: BouncerOverlayContentViewModel,
    modifier: Modifier = Modifier,
) {
    val isHeightExpanded =
        LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded

@@ -208,7 +212,7 @@ private fun StandardLayout(viewModel: BouncerSceneContentViewModel, modifier: Mo
 * by double-tapping on the side.
 */
@Composable
private fun SplitLayout(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) {
private fun SplitLayout(viewModel: BouncerOverlayContentViewModel, modifier: Modifier = Modifier) {
    val authMethod by viewModel.authMethodViewModel.collectAsStateWithLifecycle()

    Row(
@@ -297,7 +301,7 @@ private fun SplitLayout(viewModel: BouncerSceneContentViewModel, modifier: Modif
 */
@Composable
private fun BesideUserSwitcherLayout(
    viewModel: BouncerSceneContentViewModel,
    viewModel: BouncerOverlayContentViewModel,
    modifier: Modifier = Modifier,
) {
    val isLeftToRight = LocalLayoutDirection.current == LayoutDirection.Ltr
@@ -452,7 +456,7 @@ private fun BesideUserSwitcherLayout(
/** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */
@Composable
private fun BelowUserSwitcherLayout(
    viewModel: BouncerSceneContentViewModel,
    viewModel: BouncerOverlayContentViewModel,
    modifier: Modifier = Modifier,
) {
    Column(modifier = modifier.padding(vertical = 128.dp)) {
@@ -483,7 +487,7 @@ private fun BelowUserSwitcherLayout(

@Composable
private fun FoldAware(
    viewModel: BouncerSceneContentViewModel,
    viewModel: BouncerOverlayContentViewModel,
    aboveFold: @Composable BoxScope.() -> Unit,
    belowFold: @Composable BoxScope.() -> Unit,
    modifier: Modifier = Modifier,
@@ -606,7 +610,7 @@ private fun StatusMessage(viewModel: BouncerMessageViewModel, modifier: Modifier
 * For example, this can be the PIN shapes or password text field.
 */
@Composable
private fun OutputArea(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) {
private fun OutputArea(viewModel: BouncerOverlayContentViewModel, modifier: Modifier = Modifier) {
    val authMethodViewModel: AuthMethodBouncerViewModel? by
        viewModel.authMethodViewModel.collectAsStateWithLifecycle()
    when (val nonNullViewModel = authMethodViewModel) {
@@ -631,7 +635,7 @@ private fun OutputArea(viewModel: BouncerSceneContentViewModel, modifier: Modifi
 */
@Composable
private fun InputArea(
    viewModel: BouncerSceneContentViewModel,
    viewModel: BouncerOverlayContentViewModel,
    pinButtonRowVerticalSpacing: Dp,
    centerPatternDotsVertically: Boolean,
    modifier: Modifier = Modifier,
@@ -659,7 +663,7 @@ private fun InputArea(
}

@Composable
private fun ActionArea(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) {
private fun ActionArea(viewModel: BouncerOverlayContentViewModel, modifier: Modifier = Modifier) {
    val actionButton: BouncerActionButtonModel? by
        viewModel.actionButton.collectAsStateWithLifecycle()
    val appearFadeInAnimatable = remember { Animatable(0f) }
@@ -723,7 +727,7 @@ private fun ActionArea(viewModel: BouncerSceneContentViewModel, modifier: Modifi

@Composable
private fun Dialog(
    bouncerViewModel: BouncerSceneContentViewModel,
    bouncerViewModel: BouncerOverlayContentViewModel,
    dialogFactory: BouncerDialogFactory,
) {
    val dialogViewModel by bouncerViewModel.dialogViewModel.collectAsStateWithLifecycle()
@@ -751,7 +755,7 @@ private fun Dialog(

/** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
@Composable
private fun UserSwitcher(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) {
private fun UserSwitcher(viewModel: BouncerOverlayContentViewModel, modifier: Modifier = Modifier) {
    val isUserSwitcherVisible by viewModel.isUserSwitcherVisible.collectAsStateWithLifecycle()
    if (!isUserSwitcherVisible) {
        // Take up the same space as the user switcher normally would, but with nothing inside it.
@@ -829,7 +833,7 @@ private fun UserSwitcher(viewModel: BouncerSceneContentViewModel, modifier: Modi
@Composable
private fun UserSwitcherDropdownMenu(
    isExpanded: Boolean,
    items: List<BouncerSceneContentViewModel.UserSwitcherDropdownItemViewModel>,
    items: List<BouncerOverlayContentViewModel.UserSwitcherDropdownItemViewModel>,
    onDismissed: () -> Unit,
) {
    val context = LocalContext.current
+16 −16
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 * Copyright (C) 2025 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.
@@ -29,19 +29,19 @@ import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerOverlayContentViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerUserActionsViewModel
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.composable.Overlay
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow

object Bouncer {
    object Elements {
        val Root = ElementKey("BouncerRoot")
        val Background = ElementKey("BouncerBackground")
        val Content = ElementKey("BouncerContent")
    }
@@ -51,16 +51,16 @@ object Bouncer {
    }
}

/** The bouncer scene displays authentication challenges like PIN, password, or pattern. */
/** The bouncer overlay displays authentication challenges like PIN, password, or pattern. */
@SysUISingleton
class BouncerScene
class BouncerOverlay
@Inject
constructor(
    private val actionsViewModelFactory: BouncerUserActionsViewModel.Factory,
    private val contentViewModelFactory: BouncerSceneContentViewModel.Factory,
    private val contentViewModelFactory: BouncerOverlayContentViewModel.Factory,
    private val dialogFactory: BouncerDialogFactory,
) : ExclusiveActivatable(), Scene {
    override val key = Scenes.Bouncer
) : Overlay {
    override val key = Overlays.Bouncer

    private val actionsViewModel: BouncerUserActionsViewModel by lazy {
        actionsViewModelFactory.create()
@@ -68,22 +68,22 @@ constructor(

    override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions

    override suspend fun onActivated(): Nothing {
    override suspend fun activate(): Nothing {
        actionsViewModel.activate()
    }

    @Composable
    override fun ContentScope.Content(modifier: Modifier) =
        BouncerScene(
            viewModel = rememberViewModel("BouncerScene") { contentViewModelFactory.create() },
        BouncerOverlay(
            viewModel = rememberViewModel("BouncerOverlay") { contentViewModelFactory.create() },
            dialogFactory = dialogFactory,
            modifier = modifier,
            modifier = modifier.element(Bouncer.Elements.Root),
        )
}

@Composable
private fun ContentScope.BouncerScene(
    viewModel: BouncerSceneContentViewModel,
private fun ContentScope.BouncerOverlay(
    viewModel: BouncerOverlayContentViewModel,
    dialogFactory: BouncerDialogFactory,
    modifier: Modifier = Modifier,
) {
+15 −15
Original line number Diff line number Diff line
@@ -23,12 +23,12 @@ import androidx.compose.runtime.Composable
import com.android.compose.windowsizeclass.LocalWindowSizeClass

/**
 * Returns the [BouncerSceneLayout] that should be used by the bouncer scene. If
 * [isOneHandedModeSupported] is `false`, then [BouncerSceneLayout.BESIDE_USER_SWITCHER] is replaced
 * by [BouncerSceneLayout.STANDARD_BOUNCER].
 * Returns the [BouncerOverlayLayout] that should be used by the bouncer scene. If
 * [isOneHandedModeSupported] is `false`, then [BouncerOverlayLayout.BESIDE_USER_SWITCHER] is
 * replaced by [BouncerOverlayLayout.STANDARD_BOUNCER].
 */
@Composable
fun calculateLayout(isOneHandedModeSupported: Boolean): BouncerSceneLayout {
fun calculateLayout(isOneHandedModeSupported: Boolean): BouncerOverlayLayout {
    val windowSizeClass = LocalWindowSizeClass.current

    return calculateLayoutInternal(
@@ -57,7 +57,7 @@ private fun WindowHeightSizeClass.toEnum(): SizeClass {
}

/** Enumerates all known adaptive layout configurations. */
enum class BouncerSceneLayout {
enum class BouncerOverlayLayout {
    /** The default UI with the bouncer laid out normally. */
    STANDARD_BOUNCER,
    /** The bouncer is displayed vertically stacked with the user switcher. */
@@ -84,21 +84,21 @@ fun calculateLayoutInternal(
    width: SizeClass,
    height: SizeClass,
    isOneHandedModeSupported: Boolean,
): BouncerSceneLayout {
): BouncerOverlayLayout {
    return when (height) {
        SizeClass.COMPACT -> BouncerSceneLayout.SPLIT_BOUNCER
        SizeClass.COMPACT -> BouncerOverlayLayout.SPLIT_BOUNCER
        SizeClass.MEDIUM ->
            when (width) {
                SizeClass.COMPACT -> BouncerSceneLayout.STANDARD_BOUNCER
                SizeClass.MEDIUM -> BouncerSceneLayout.STANDARD_BOUNCER
                SizeClass.EXPANDED -> BouncerSceneLayout.BESIDE_USER_SWITCHER
                SizeClass.COMPACT -> BouncerOverlayLayout.STANDARD_BOUNCER
                SizeClass.MEDIUM -> BouncerOverlayLayout.STANDARD_BOUNCER
                SizeClass.EXPANDED -> BouncerOverlayLayout.BESIDE_USER_SWITCHER
            }
        SizeClass.EXPANDED ->
            when (width) {
                SizeClass.COMPACT -> BouncerSceneLayout.STANDARD_BOUNCER
                SizeClass.MEDIUM -> BouncerSceneLayout.BELOW_USER_SWITCHER
                SizeClass.EXPANDED -> BouncerSceneLayout.BESIDE_USER_SWITCHER
                SizeClass.COMPACT -> BouncerOverlayLayout.STANDARD_BOUNCER
                SizeClass.MEDIUM -> BouncerOverlayLayout.BELOW_USER_SWITCHER
                SizeClass.EXPANDED -> BouncerOverlayLayout.BESIDE_USER_SWITCHER
            }
    }.takeIf { it != BouncerSceneLayout.BESIDE_USER_SWITCHER || isOneHandedModeSupported }
        ?: BouncerSceneLayout.STANDARD_BOUNCER
    }.takeIf { it != BouncerOverlayLayout.BESIDE_USER_SWITCHER || isOneHandedModeSupported }
        ?: BouncerOverlayLayout.STANDARD_BOUNCER
}
+3 −2
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.systemui.scene.shared.model.Overlays
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
@@ -100,7 +101,7 @@ fun ContentScope.NotificationLockscreenScrim(

    val isBouncerToLockscreen =
        layoutState.currentTransition?.isTransitioning(
            from = Scenes.Bouncer,
            from = Overlays.Bouncer,
            to = Scenes.Lockscreen,
        ) ?: false

@@ -120,5 +121,5 @@ private fun shouldShowScrimFadeOut(
    return shadeMode != ShadeMode.Dual &&
        currentTransition.isInitiatedByUserInput &&
        (currentTransition.isTransitioning(from = Scenes.Shade, to = Scenes.Lockscreen) ||
            currentTransition.isTransitioning(from = Scenes.Bouncer, to = Scenes.Lockscreen))
            currentTransition.isTransitioning(from = Overlays.Bouncer, to = Scenes.Lockscreen))
}
Loading