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

Commit 0d341f29 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[flexiglass] Bouncer scene adaptive layout improvements.

1. Better breakdown into "layout" and "area" functions.
2. Broke down the dependency on "StandardLayout" where all layouts
   eventually called "StandardLayout". Now each layout arranges its own
   "area" element functions directly.
3. Cleaned up foldability functionality, which is still used by both
   "standard" and "beside user switcher" layouts.
4. Better naming sceheme for layouts: "stacked" renamed to "below user
   switcher" and "side-by-side" renamed to "beside user switcher".

Bug: 300677757
Test: manually verified on 4 physical devices: phone, foldables, taller
foldable, and tablet.
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT

Change-Id: I4d21f7acf0acef64bad335ff2d045753a66ed7d0
parent 42f9b0d5
Loading
Loading
Loading
Loading
+505 −443

File changed.

Preview size limit exceeded, changes collapsed.

+2 −2
Original line number Original line Diff line number Diff line
@@ -26,8 +26,8 @@ import com.android.systemui.bouncer.ui.helper.calculateLayoutInternal


/**
/**
 * Returns the [BouncerSceneLayout] that should be used by the bouncer scene. If
 * Returns the [BouncerSceneLayout] that should be used by the bouncer scene. If
 * [isSideBySideSupported] is `false`, then [BouncerSceneLayout.SIDE_BY_SIDE] is replaced by
 * [isSideBySideSupported] is `false`, then [BouncerSceneLayout.BESIDE_USER_SWITCHER] is replaced by
 * [BouncerSceneLayout.STANDARD].
 * [BouncerSceneLayout.STANDARD_BOUNCER].
 */
 */
@Composable
@Composable
fun calculateLayout(
fun calculateLayout(
+31 −37
Original line number Original line Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.systemui.bouncer.ui.composable
package com.android.systemui.bouncer.ui.composable


import android.view.ViewTreeObserver
import android.view.ViewTreeObserver
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.LocalTextStyle
@@ -31,7 +30,6 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.FocusRequester
@@ -81,10 +79,6 @@ internal fun PasswordBouncer(
        }
        }
    }
    }


    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = modifier,
    ) {
    val color = MaterialTheme.colorScheme.onSurfaceVariant
    val color = MaterialTheme.colorScheme.onSurfaceVariant
    val lineWidthPx = with(LocalDensity.current) { 2.dp.toPx() }
    val lineWidthPx = with(LocalDensity.current) { 2.dp.toPx() }


@@ -105,7 +99,8 @@ internal fun PasswordBouncer(
                onDone = { viewModel.onAuthenticateKeyPressed() },
                onDone = { viewModel.onAuthenticateKeyPressed() },
            ),
            ),
        modifier =
        modifier =
                Modifier.focusRequester(focusRequester)
            modifier
                .focusRequester(focusRequester)
                .onFocusChanged { viewModel.onTextFieldFocusChanged(it.isFocused) }
                .onFocusChanged { viewModel.onTextFieldFocusChanged(it.isFocused) }
                .drawBehind {
                .drawBehind {
                    drawLine(
                    drawLine(
@@ -117,7 +112,6 @@ internal fun PasswordBouncer(
                },
                },
    )
    )
}
}
}


/** Returns a [State] with `true` when the IME/keyboard is visible and `false` when it's not. */
/** Returns a [State] with `true` when the IME/keyboard is visible and `false` when it's not. */
@Composable
@Composable
+15 −7
Original line number Original line Diff line number Diff line
@@ -24,6 +24,8 @@ import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.DisposableEffect
@@ -48,7 +50,6 @@ import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings
import com.android.compose.animation.Easings
import com.android.compose.modifiers.thenIf
import com.android.compose.modifiers.thenIf
import com.android.internal.R
import com.android.internal.R
import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PatternDotViewModel
import com.android.systemui.bouncer.ui.viewmodel.PatternDotViewModel
import kotlin.math.min
import kotlin.math.min
@@ -61,11 +62,14 @@ import kotlinx.coroutines.launch
 * UI for the input part of a pattern-requiring version of the bouncer.
 * UI for the input part of a pattern-requiring version of the bouncer.
 *
 *
 * The user can press, hold, and drag their pointer to select dots along a grid of dots.
 * The user can press, hold, and drag their pointer to select dots along a grid of dots.
 *
 * If [centerDotsVertically] is `true`, the dots should be centered along the axis of interest; if
 * `false`, the dots will be pushed towards the end/bottom of the axis.
 */
 */
@Composable
@Composable
internal fun PatternBouncer(
internal fun PatternBouncer(
    viewModel: PatternBouncerViewModel,
    viewModel: PatternBouncerViewModel,
    layout: BouncerSceneLayout,
    centerDotsVertically: Boolean,
    modifier: Modifier = Modifier,
    modifier: Modifier = Modifier,
) {
) {
    DisposableEffect(Unit) {
    DisposableEffect(Unit) {
@@ -197,6 +201,14 @@ internal fun PatternBouncer(


    Canvas(
    Canvas(
        modifier
        modifier
            // Because the width also includes spacing to the left and right of the leftmost and
            // rightmost dots in the grid and because UX mocks specify the width without that
            // spacing, the actual width needs to be defined slightly bigger than the UX mock width.
            .width((262 * colCount / 2).dp)
            // Because the height also includes spacing above and below the topmost and bottommost
            // dots in the grid and because UX mocks specify the height without that spacing, the
            // actual height needs to be defined slightly bigger than the UX mock height.
            .height((262 * rowCount / 2).dp)
            // Need to clip to bounds to make sure that the lines don't follow the input pointer
            // Need to clip to bounds to make sure that the lines don't follow the input pointer
            // when it leaves the bounds of the dot grid.
            // when it leaves the bounds of the dot grid.
            .clipToBounds()
            .clipToBounds()
@@ -260,7 +272,7 @@ internal fun PatternBouncer(
                    availableSize = containerSize.height,
                    availableSize = containerSize.height,
                    spacingPerDot = spacing,
                    spacingPerDot = spacing,
                    dotCount = rowCount,
                    dotCount = rowCount,
                    isCentered = layout.isCenteredVertically,
                    isCentered = centerDotsVertically,
                )
                )
            offset = Offset(horizontalOffset, verticalOffset)
            offset = Offset(horizontalOffset, verticalOffset)
            scale = (colCount * spacing) / containerSize.width
            scale = (colCount * spacing) / containerSize.width
@@ -423,10 +435,6 @@ private fun offset(
    }
    }
}
}


/** Whether the UI should be centered vertically. */
private val BouncerSceneLayout.isCenteredVertically: Boolean
    get() = this == BouncerSceneLayout.SPLIT

private const val DOT_DIAMETER_DP = 16
private const val DOT_DIAMETER_DP = 16
private const val SELECTED_DOT_DIAMETER_DP = 24
private const val SELECTED_DOT_DIAMETER_DP = 24
private const val SELECTED_DOT_REACTION_ANIMATION_DURATION_MS = 83
private const val SELECTED_DOT_REACTION_ANIMATION_DURATION_MS = 83
+3 −12
Original line number Original line Diff line number Diff line
@@ -52,7 +52,6 @@ import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings
import com.android.compose.animation.Easings
import com.android.compose.grid.VerticalGrid
import com.android.compose.grid.VerticalGrid
import com.android.compose.modifiers.thenIf
import com.android.compose.modifiers.thenIf
import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance
import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.ContentDescription
@@ -70,7 +69,7 @@ import kotlinx.coroutines.launch
@Composable
@Composable
fun PinPad(
fun PinPad(
    viewModel: PinBouncerViewModel,
    viewModel: PinBouncerViewModel,
    layout: BouncerSceneLayout,
    verticalSpacing: Dp,
    modifier: Modifier = Modifier,
    modifier: Modifier = Modifier,
) {
) {
    DisposableEffect(Unit) {
    DisposableEffect(Unit) {
@@ -96,8 +95,8 @@ fun PinPad(


    VerticalGrid(
    VerticalGrid(
        columns = columns,
        columns = columns,
        verticalSpacing = layout.verticalSpacing,
        verticalSpacing = verticalSpacing,
        horizontalSpacing = calculateHorizontalSpacingBetweenColumns(layout.gridWidth),
        horizontalSpacing = calculateHorizontalSpacingBetweenColumns(gridWidth = 300.dp),
        modifier = modifier,
        modifier = modifier,
    ) {
    ) {
        repeat(9) { index ->
        repeat(9) { index ->
@@ -355,14 +354,6 @@ private fun calculateHorizontalSpacingBetweenColumns(
    return (gridWidth - (pinButtonMaxSize * columns)) / (columns - 1)
    return (gridWidth - (pinButtonMaxSize * columns)) / (columns - 1)
}
}


/** The width of the grid of PIN pad buttons, in dips. */
private val BouncerSceneLayout.gridWidth: Dp
    get() = if (isUseCompactSize) 292.dp else 300.dp

/** The spacing between rows of PIN pad buttons, in dips. */
private val BouncerSceneLayout.verticalSpacing: Dp
    get() = if (isUseCompactSize) 8.dp else 12.dp

/** Number of columns in the PIN pad grid. */
/** Number of columns in the PIN pad grid. */
private const val columns = 3
private const val columns = 3
/** Maximum size (width and height) of each PIN pad button. */
/** Maximum size (width and height) of each PIN pad button. */
Loading