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

Commit 14a7b0a8 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[flexiglass] Splits up PIN user input when split layout.

When using the PIN auth method on a folded device in landscape mode, we
get the split layout, where half the bouncer is one side and the other
half is on the other; the PIN entry area (the keypad) needs to be on the
right but the PIN output area (where the dots and shapes appear as the
user enters the PIN) needs to be on the other side, between the message
and emergency button.

Bug: 304630105
Test: please see comment #5 on the attached bug
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT
Change-Id: Ia80e011dd6de25128aff072f8d1fe5d0843bbbcf
parent 455b0e8b
Loading
Loading
Loading
Loading
+71 −24
Original line number Diff line number Diff line
@@ -154,7 +154,7 @@ private fun SceneScope.BouncerScene(
                Bouncer(
                    viewModel = viewModel,
                    dialogFactory = dialogFactory,
                    isUserInputAreaVisible = true,
                    userInputAreaVisibility = UserInputAreaVisibility.FULL,
                    modifier = childModifier,
                )
            Layout.SIDE_BY_SIDE ->
@@ -189,7 +189,7 @@ private fun SceneScope.BouncerScene(
private fun Bouncer(
    viewModel: BouncerViewModel,
    dialogFactory: BouncerSceneDialogFactory,
    isUserInputAreaVisible: Boolean,
    userInputAreaVisibility: UserInputAreaVisibility,
    modifier: Modifier = Modifier,
) {
    val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
@@ -214,13 +214,12 @@ private fun Bouncer(
        }

        Box(Modifier.weight(1f)) {
            if (isUserInputAreaVisible) {
            UserInputArea(
                viewModel = viewModel,
                visibility = userInputAreaVisibility,
                modifier = Modifier.align(Alignment.Center),
            )
        }
        }

        if (viewModel.isEmergencyButtonVisible) {
            Button(
@@ -269,6 +268,7 @@ private fun Bouncer(
@Composable
private fun UserInputArea(
    viewModel: BouncerViewModel,
    visibility: UserInputAreaVisibility,
    modifier: Modifier = Modifier,
) {
    val authMethodViewModel: AuthMethodBouncerViewModel? by
@@ -276,21 +276,46 @@ private fun UserInputArea(

    when (val nonNullViewModel = authMethodViewModel) {
        is PinBouncerViewModel ->
            when (visibility) {
                UserInputAreaVisibility.FULL ->
                    PinBouncer(
                        viewModel = nonNullViewModel,
                        modifier = modifier,
                    )
                UserInputAreaVisibility.INPUT_ONLY ->
                    PinPad(
                        viewModel = nonNullViewModel,
                        modifier = modifier,
                    )
                UserInputAreaVisibility.OUTPUT_ONLY ->
                    PinInputDisplay(
                        viewModel = nonNullViewModel,
                        modifier = modifier,
                    )
                UserInputAreaVisibility.NONE -> {}
            }
        is PasswordBouncerViewModel ->
            when (visibility) {
                UserInputAreaVisibility.FULL,
                UserInputAreaVisibility.INPUT_ONLY ->
                    PasswordBouncer(
                        viewModel = nonNullViewModel,
                        modifier = modifier,
                    )
                else -> {}
            }
        is PatternBouncerViewModel ->
            when (visibility) {
                UserInputAreaVisibility.FULL,
                UserInputAreaVisibility.INPUT_ONLY ->
                    PatternBouncer(
                        viewModel = nonNullViewModel,
                        modifier =
                    Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false).then(modifier)
                            Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
                                .then(modifier)
                    )
                else -> {}
            }
        else -> Unit
    }
}
@@ -435,13 +460,14 @@ private fun Split(
            Bouncer(
                viewModel = viewModel,
                dialogFactory = dialogFactory,
                isUserInputAreaVisible = false,
                userInputAreaVisibility = UserInputAreaVisibility.OUTPUT_ONLY,
                modifier = startContentModifier,
            )
        },
        endContent = { endContentModifier ->
            UserInputArea(
                viewModel = viewModel,
                visibility = UserInputAreaVisibility.INPUT_ONLY,
                modifier = endContentModifier,
            )
        },
@@ -545,7 +571,7 @@ private fun SideBySide(
            Bouncer(
                viewModel = viewModel,
                dialogFactory = dialogFactory,
                isUserInputAreaVisible = true,
                userInputAreaVisibility = UserInputAreaVisibility.FULL,
                modifier = endContentModifier,
            )
        },
@@ -574,7 +600,7 @@ private fun Stacked(
        Bouncer(
            viewModel = viewModel,
            dialogFactory = dialogFactory,
            isUserInputAreaVisible = true,
            userInputAreaVisibility = UserInputAreaVisibility.FULL,
            modifier = Modifier.fillMaxWidth().weight(1f),
        )
    }
@@ -630,6 +656,27 @@ private enum class Layout {
    SPLIT,
}

/** Enumerates all supported user-input area visibilities. */
private enum class UserInputAreaVisibility {
    /**
     * The entire user input area is shown, including where the user enters input and where it's
     * reflected to the user.
     */
    FULL,
    /**
     * Only the area where the user enters the input is shown; the area where the input is reflected
     * back to the user is not shown.
     */
    INPUT_ONLY,
    /**
     * Only the area where the input is reflected back to the user is shown; the area where the
     * input is entered by the user is not shown.
     */
    OUTPUT_ONLY,
    /** The entire user input area is hidden. */
    NONE,
}

/**
 * Calculates an alpha for the user switcher and bouncer such that it's at `1` when the offset of
 * the two reaches a stopping point but `0` in the middle of the transition.
+6 −2
Original line number Diff line number Diff line
@@ -55,12 +55,12 @@ import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings
import com.android.compose.grid.VerticalGrid
import com.android.compose.modifiers.thenIf
import com.android.systemui.res.R
import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.res.R
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.DurationUnit
import kotlinx.coroutines.async
@@ -93,7 +93,10 @@ internal fun PinBouncer(
}

@Composable
private fun PinPad(viewModel: PinBouncerViewModel) {
fun PinPad(
    viewModel: PinBouncerViewModel,
    modifier: Modifier = Modifier,
) {
    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
    val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState()
    val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsState()
@@ -112,6 +115,7 @@ private fun PinPad(viewModel: PinBouncerViewModel) {
        columns = 3,
        verticalSpacing = 12.dp,
        horizontalSpacing = 20.dp,
        modifier = modifier,
    ) {
        repeat(9) { index ->
            DigitButton(
+14 −8
Original line number Diff line number Diff line
@@ -51,10 +51,10 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings
import com.android.keyguard.PinShapeAdapter
import com.android.systemui.res.R
import com.android.systemui.bouncer.ui.viewmodel.EntryToken.Digit
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinInputViewModel
import com.android.systemui.res.R
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
@@ -65,7 +65,10 @@ import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch

@Composable
fun PinInputDisplay(viewModel: PinBouncerViewModel) {
fun PinInputDisplay(
    viewModel: PinBouncerViewModel,
    modifier: Modifier = Modifier,
) {
    val hintedPinLength: Int? by viewModel.hintedPinLength.collectAsState()
    val shapeAnimations = rememberShapeAnimations(viewModel.pinShapes)

@@ -81,8 +84,8 @@ fun PinInputDisplay(viewModel: PinBouncerViewModel) {
    // unifying into a single, more complex implementation.

    when (val length = hintedPinLength) {
        null -> RegularPinInputDisplay(viewModel, shapeAnimations)
        else -> HintingPinInputDisplay(viewModel, shapeAnimations, length)
        null -> RegularPinInputDisplay(viewModel, shapeAnimations, modifier)
        else -> HintingPinInputDisplay(viewModel, shapeAnimations, length, modifier)
    }
}

@@ -97,6 +100,7 @@ private fun HintingPinInputDisplay(
    viewModel: PinBouncerViewModel,
    shapeAnimations: ShapeAnimations,
    hintedPinLength: Int,
    modifier: Modifier = Modifier,
) {
    val pinInput: PinInputViewModel by viewModel.pinInput.collectAsState()
    // [ClearAll] marker pointing at the beginning of the current pin input.
@@ -151,7 +155,7 @@ private fun HintingPinInputDisplay(
    LaunchedEffect(Unit) { playAnimation = true }

    val dotColor = MaterialTheme.colorScheme.onSurfaceVariant
    Row(modifier = Modifier.heightIn(min = shapeAnimations.shapeSize)) {
    Row(modifier = modifier.heightIn(min = shapeAnimations.shapeSize)) {
        pinEntryDrawable.forEachIndexed { index, drawable ->
            // Key the loop by [index] and [drawable], so that updating a shape drawable at the same
            // index will play the new animation (by remembering a new [atEnd]).
@@ -183,6 +187,7 @@ private fun HintingPinInputDisplay(
private fun RegularPinInputDisplay(
    viewModel: PinBouncerViewModel,
    shapeAnimations: ShapeAnimations,
    modifier: Modifier = Modifier,
) {
    // Holds all currently [VisiblePinEntry] composables. This cannot be simply derived from
    // `viewModel.pinInput` at composition, since deleting a pin entry needs to play a remove
@@ -226,7 +231,7 @@ private fun RegularPinInputDisplay(
            }
    }

    pinInputRow.Content()
    pinInputRow.Content(modifier)
}

private class PinInputRow(
@@ -235,10 +240,11 @@ private class PinInputRow(
    private val entries = mutableStateListOf<PinInputEntry>()

    @Composable
    fun Content() {
    fun Content(modifier: Modifier) {
        Row(
            modifier =
                Modifier.heightIn(min = shapeAnimations.shapeSize)
                modifier
                    .heightIn(min = shapeAnimations.shapeSize)
                    // Pins overflowing horizontally should still be shown as scrolling.
                    .wrapContentSize(unbounded = true),
        ) {