Loading packages/SystemUI/aconfig/desktop_users_and_accounts.aconfig +0 −7 Original line number Diff line number Diff line Loading @@ -3,13 +3,6 @@ container: "system" # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors. flag { name: "back_button_on_bouncer" namespace: "desktop_users_and_accounts" description: "Add a 'back' button to bouncer" bug: "392597328" } flag { name: "disable_double_click_swap_on_bouncer" namespace: "desktop_users_and_accounts" Loading packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt +20 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.KeyboardArrowDown import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.DropdownMenu Loading @@ -55,6 +56,7 @@ import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect Loading Loading @@ -253,6 +255,24 @@ fun ContentScope.BouncerContent( } Dialog(bouncerViewModel = viewModel, dialogFactory = dialogFactory) if (viewModel.showBackButton) { TextButton( onClick = viewModel::navigateBack, modifier = Modifier.align(Alignment.BottomStart) .padding(horizontal = 48.dp, vertical = 40.dp) .testTag("BackButton"), ) { Icon( Icons.AutoMirrored.Filled.ArrowBack, null, tint = MaterialTheme.colorScheme.primary, ) Spacer(Modifier.size(ButtonDefaults.IconSpacing)) Text(text = stringResource(R.string.back_button_on_bouncer), fontSize = 14.sp) } } } } Loading packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerOverlayContentViewModelTest.kt +19 −0 Original line number Diff line number Diff line Loading @@ -38,11 +38,14 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.shared.model.DismissAction import com.android.systemui.keyguard.shared.model.KeyguardDone import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runCurrent import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.domain.startable.sceneContainerStartable import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage Loading Loading @@ -234,6 +237,22 @@ class BouncerOverlayContentViewModelTest : SysuiTestCase() { assertThat(dismissAction).isEqualTo(DismissAction.None) } @Test fun navigateBack_hidesBouncerOverlay() = kosmos.runTest { val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) // Show bouncer sceneInteractor.showOverlay(Overlays.Bouncer, "reason") runCurrent() assertThat(currentOverlays).contains(Overlays.Bouncer) // Navigate back underTest.navigateBack() runCurrent() assertThat(currentOverlays).doesNotContain(Overlays.Bouncer) } private fun authMethodsToTest(): List<AuthenticationMethodModel> { return listOf(None, Pin, Password, Pattern, Sim) } Loading packages/SystemUI/res/values/strings.xml +3 −0 Original line number Diff line number Diff line Loading @@ -4422,4 +4422,7 @@ right edge of the display to expand the Quick Settings shade panel. --> <string name="dual_shade_educational_tooltip_qs">Swipe from the top right to open Quick Settings</string> <!-- Content of button shown on the bouncer screen to navigate back to keyguard. [CHAR LIMIT=NONE] --> <string name="back_button_on_bouncer">Back</string> </resources> packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerOverlayContentViewModel.kt +13 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInte import com.android.systemui.keyguard.domain.interactor.KeyguardMediaKeyInteractor import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject Loading Loading @@ -71,6 +73,7 @@ constructor( private val keyguardMediaKeyInteractor: KeyguardMediaKeyInteractor, private val bouncerActionButtonInteractor: BouncerActionButtonInteractor, private val keyguardDismissActionInteractor: KeyguardDismissActionInteractor, private val sceneInteractor: SceneInteractor, ) : ExclusiveActivatable() { private val _selectedUserImage = MutableStateFlow<Bitmap?>(null) val selectedUserImage: StateFlow<Bitmap?> = _selectedUserImage.asStateFlow() Loading Loading @@ -134,6 +137,12 @@ constructor( */ val isOneHandedModeSupported: StateFlow<Boolean> = _isOneHandedModeSupported.asStateFlow() /** * Whether to show a "back" button on bouncer. This is enabled for large screen interaction as * these typically don't rely on touch gestures to go back. */ val showBackButton = bouncerInteractor.isImproveLargeScreenInteractionEnabled private val _isInputPreferredOnLeftSide = MutableStateFlow(false) val isInputPreferredOnLeftSide = _isInputPreferredOnLeftSide.asStateFlow() Loading Loading @@ -460,6 +469,10 @@ constructor( keyguardDismissActionInteractor.clearDismissAction() } fun navigateBack() { sceneInteractor.hideOverlay(Overlays.Bouncer, "back button clicked") } data class DialogViewModel( val text: String, Loading Loading
packages/SystemUI/aconfig/desktop_users_and_accounts.aconfig +0 −7 Original line number Diff line number Diff line Loading @@ -3,13 +3,6 @@ container: "system" # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors. flag { name: "back_button_on_bouncer" namespace: "desktop_users_and_accounts" description: "Add a 'back' button to bouncer" bug: "392597328" } flag { name: "disable_double_click_swap_on_bouncer" namespace: "desktop_users_and_accounts" Loading
packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt +20 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.KeyboardArrowDown import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.DropdownMenu Loading @@ -55,6 +56,7 @@ import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect Loading Loading @@ -253,6 +255,24 @@ fun ContentScope.BouncerContent( } Dialog(bouncerViewModel = viewModel, dialogFactory = dialogFactory) if (viewModel.showBackButton) { TextButton( onClick = viewModel::navigateBack, modifier = Modifier.align(Alignment.BottomStart) .padding(horizontal = 48.dp, vertical = 40.dp) .testTag("BackButton"), ) { Icon( Icons.AutoMirrored.Filled.ArrowBack, null, tint = MaterialTheme.colorScheme.primary, ) Spacer(Modifier.size(ButtonDefaults.IconSpacing)) Text(text = stringResource(R.string.back_button_on_bouncer), fontSize = 14.sp) } } } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerOverlayContentViewModelTest.kt +19 −0 Original line number Diff line number Diff line Loading @@ -38,11 +38,14 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.shared.model.DismissAction import com.android.systemui.keyguard.shared.model.KeyguardDone import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runCurrent import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.domain.startable.sceneContainerStartable import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage Loading Loading @@ -234,6 +237,22 @@ class BouncerOverlayContentViewModelTest : SysuiTestCase() { assertThat(dismissAction).isEqualTo(DismissAction.None) } @Test fun navigateBack_hidesBouncerOverlay() = kosmos.runTest { val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) // Show bouncer sceneInteractor.showOverlay(Overlays.Bouncer, "reason") runCurrent() assertThat(currentOverlays).contains(Overlays.Bouncer) // Navigate back underTest.navigateBack() runCurrent() assertThat(currentOverlays).doesNotContain(Overlays.Bouncer) } private fun authMethodsToTest(): List<AuthenticationMethodModel> { return listOf(None, Pin, Password, Pattern, Sim) } Loading
packages/SystemUI/res/values/strings.xml +3 −0 Original line number Diff line number Diff line Loading @@ -4422,4 +4422,7 @@ right edge of the display to expand the Quick Settings shade panel. --> <string name="dual_shade_educational_tooltip_qs">Swipe from the top right to open Quick Settings</string> <!-- Content of button shown on the bouncer screen to navigate back to keyguard. [CHAR LIMIT=NONE] --> <string name="back_button_on_bouncer">Back</string> </resources>
packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerOverlayContentViewModel.kt +13 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInte import com.android.systemui.keyguard.domain.interactor.KeyguardMediaKeyInteractor import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject Loading Loading @@ -71,6 +73,7 @@ constructor( private val keyguardMediaKeyInteractor: KeyguardMediaKeyInteractor, private val bouncerActionButtonInteractor: BouncerActionButtonInteractor, private val keyguardDismissActionInteractor: KeyguardDismissActionInteractor, private val sceneInteractor: SceneInteractor, ) : ExclusiveActivatable() { private val _selectedUserImage = MutableStateFlow<Bitmap?>(null) val selectedUserImage: StateFlow<Bitmap?> = _selectedUserImage.asStateFlow() Loading Loading @@ -134,6 +137,12 @@ constructor( */ val isOneHandedModeSupported: StateFlow<Boolean> = _isOneHandedModeSupported.asStateFlow() /** * Whether to show a "back" button on bouncer. This is enabled for large screen interaction as * these typically don't rely on touch gestures to go back. */ val showBackButton = bouncerInteractor.isImproveLargeScreenInteractionEnabled private val _isInputPreferredOnLeftSide = MutableStateFlow(false) val isInputPreferredOnLeftSide = _isInputPreferredOnLeftSide.asStateFlow() Loading Loading @@ -460,6 +469,10 @@ constructor( keyguardDismissActionInteractor.clearDismissAction() } fun navigateBack() { sceneInteractor.hideOverlay(Overlays.Bouncer, "back button clicked") } data class DialogViewModel( val text: String, Loading