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

Commit da276c76 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[flexiglass] Password bouncer isn't side-by-side on unfolded.

When the auth method is password and a foldable device is unfolded, the
chosen layout is the standard layout and not the side-by-side layout.

Bug: 304630105
Test: please see comment #6 on the attached bug
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT
Change-Id: Ie15da0f615db18e46b1f1df81d4780b9c7159c72
parent 08495046
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -142,7 +142,11 @@ private fun SceneScope.BouncerScene(
    modifier: Modifier = Modifier,
) {
    val backgroundColor = MaterialTheme.colorScheme.surface
    val layout = calculateLayout()
    val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsState()
    val layout =
        calculateLayout(
            isSideBySideSupported = isSideBySideSupported,
        )

    Box(modifier) {
        Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) {
@@ -567,6 +571,11 @@ private fun SwappableLayout(
/**
 * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap
 * anywhere on the background to flip their positions.
 *
 * In situations when [isUserSwitcherVisible] is `false`, one of two things may happen: either the
 * UI for the bouncer will be shown on its own, taking up one side, with the other side just being
 * empty space or, if that kind of "stand-alone side-by-side" isn't supported, the standard
 * rendering of the bouncer will be used instead of the side-by-side layout.
 */
@Composable
private fun SideBySide(
@@ -628,7 +637,9 @@ private fun Stacked(
}

@Composable
private fun calculateLayout(): Layout {
private fun calculateLayout(
    isSideBySideSupported: Boolean,
): Layout {
    val windowSizeClass = LocalWindowSizeClass.current
    val width = windowSizeClass.widthSizeClass
    val height = windowSizeClass.heightSizeClass
@@ -657,7 +668,7 @@ private fun calculateLayout(): Layout {
        // Large and tall devices (i.e. tablet in portrait).
        isTall -> Layout.STACKED
        // Large and wide/square devices (i.e. tablet in landscape, unfolded).
        else -> Layout.SIDE_BY_SIDE
        else -> if (isSideBySideSupported) Layout.SIDE_BY_SIDE else Layout.STANDARD
    }
}

+3 −2
Original line number Diff line number Diff line
@@ -29,14 +29,15 @@ import kotlinx.coroutines.flow.asStateFlow
class BouncerRepository
@Inject
constructor(
    flags: FeatureFlagsClassic,
    private val flags: FeatureFlagsClassic,
) {
    private val _message = MutableStateFlow<String?>(null)
    /** The user-facing message to show in the bouncer. */
    val message: StateFlow<String?> = _message.asStateFlow()

    /** Whether the user switcher should be displayed within the bouncer UI on large screens. */
    val isUserSwitcherVisible: Boolean = flags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)
    val isUserSwitcherVisible: Boolean
        get() = flags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)

    fun setMessage(message: String?) {
        _message.value = message
+2 −1
Original line number Diff line number Diff line
@@ -97,7 +97,8 @@ constructor(
    val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible

    /** Whether the user switcher should be displayed within the bouncer UI on large screens. */
    val isUserSwitcherVisible: Boolean = repository.isUserSwitcherVisible
    val isUserSwitcherVisible: Boolean
        get() = repository.isUserSwitcherVisible

    init {
        if (flags.isEnabled()) {
+23 −1
Original line number Diff line number Diff line
@@ -100,7 +100,8 @@ class BouncerViewModel(
                initialValue = emptyList(),
            )

    val isUserSwitcherVisible: Boolean = bouncerInteractor.isUserSwitcherVisible
    val isUserSwitcherVisible: Boolean
        get() = bouncerInteractor.isUserSwitcherVisible

    private val isInputEnabled: StateFlow<Boolean> =
        bouncerInteractor.isThrottled
@@ -162,6 +163,23 @@ class BouncerViewModel(
            initialValue = null
        )

    /**
     * Whether the "side-by-side" layout is supported.
     *
     * When presented on its own, without a user switcher (e.g. not on communal devices like
     * tablets, for example), some authentication method UIs don't do well if they're shown in the
     * side-by-side layout; these need to be shown with the standard layout so they can take up as
     * much width as possible.
     */
    val isSideBySideSupported: StateFlow<Boolean> =
        authMethodViewModel
            .map { authMethod -> isSideBySideSupported(authMethod) }
            .stateIn(
                scope = applicationScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = isSideBySideSupported(authMethodViewModel.value),
            )

    init {
        if (flags.isEnabled()) {
            applicationScope.launch {
@@ -190,6 +208,10 @@ class BouncerViewModel(
        _throttlingDialogMessage.value = null
    }

    private fun isSideBySideSupported(authMethod: AuthMethodBouncerViewModel?): Boolean {
        return isUserSwitcherVisible || authMethod !is PasswordBouncerViewModel
    }

    private fun toMessageViewModel(
        message: String?,
        isThrottled: Boolean,
+24 −0
Original line number Diff line number Diff line
@@ -20,9 +20,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.scene.SceneTestUtils
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
@@ -202,6 +204,28 @@ class BouncerViewModelTest : SysuiTestCase() {
            assertThat(throttlingDialogMessage).isNull()
        }

    @Test
    fun isSideBySideSupported() =
        testScope.runTest {
            val isSideBySideSupported by collectLastValue(underTest.isSideBySideSupported)
            utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
            assertThat(isSideBySideSupported).isTrue()
            utils.authenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Password
            )
            assertThat(isSideBySideSupported).isTrue()

            utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
            assertThat(isSideBySideSupported).isTrue()

            utils.authenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Password
            )
            assertThat(isSideBySideSupported).isFalse()
        }

    private fun authMethodsToTest(): List<DomainLayerAuthenticationMethodModel> {
        return listOf(
            DomainLayerAuthenticationMethodModel.None,