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

Commit bd1a63d2 authored by Ale Nijamkin's avatar Ale Nijamkin Committed by Alejandro Nijamkin
Browse files

[flexiglass] Revert^2 of Bouncer scene large screen support

This reverts commit 938618d4.
Test: see original CL

Change-Id: I656898c7703d1c29b56c97d8895d2185fba4540a
parent 006396ac
Loading
Loading
Loading
Loading
+222 −78
Original line number Diff line number Diff line
@@ -14,40 +14,49 @@
 * limitations under the License.
 */

@file:OptIn(ExperimentalMaterial3Api::class)

package com.android.systemui.bouncer.ui.composable

import android.app.AlertDialog
import android.app.Dialog
import android.content.DialogInterface
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
@@ -103,25 +112,59 @@ private fun SceneScope.BouncerScene(
    dialogFactory: BouncerSceneDialogFactory,
    modifier: Modifier = Modifier,
) {
    val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
    val authMethodViewModel: AuthMethodBouncerViewModel? by
        viewModel.authMethodViewModel.collectAsState()
    val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
    var dialog: Dialog? by remember { mutableStateOf(null) }
    val backgroundColor = MaterialTheme.colorScheme.surface
    val windowSizeClass = LocalWindowSizeClass.current

    Box(modifier) {
        Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) {
            drawRect(color = backgroundColor)
        }

        val childModifier = Modifier.element(Bouncer.Elements.Content).fillMaxSize()

        when (windowSizeClass.widthSizeClass) {
            WindowWidthSizeClass.Expanded ->
                SideBySide(
                    viewModel = viewModel,
                    dialogFactory = dialogFactory,
                    modifier = childModifier,
                )
            WindowWidthSizeClass.Medium ->
                Stacked(
                    viewModel = viewModel,
                    dialogFactory = dialogFactory,
                    modifier = childModifier,
                )
            else ->
                Bouncer(
                    viewModel = viewModel,
                    dialogFactory = dialogFactory,
                    modifier = childModifier,
                )
        }
    }
}

/**
 * Renders the contents of the actual bouncer UI, the area that takes user input to do an
 * authentication attempt, including all messaging UI (directives, reasoning, errors, etc.).
 */
@Composable
private fun Bouncer(
    viewModel: BouncerViewModel,
    dialogFactory: BouncerSceneDialogFactory,
    modifier: Modifier = Modifier,
) {
    val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
    val authMethodViewModel: AuthMethodBouncerViewModel? by
        viewModel.authMethodViewModel.collectAsState()
    val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
    var dialog: Dialog? by remember { mutableStateOf(null) }

    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.spacedBy(60.dp),
            modifier =
                Modifier.element(Bouncer.Elements.Content)
                    .fillMaxSize()
                    .padding(start = 32.dp, top = 92.dp, end = 32.dp, bottom = 32.dp)
        modifier = modifier.padding(start = 32.dp, top = 92.dp, end = 32.dp, bottom = 32.dp)
    ) {
        Crossfade(
            targetState = message,
@@ -194,6 +237,107 @@ private fun SceneScope.BouncerScene(
        }
    }
}

/** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
@Composable
private fun UserSwitcher(
    modifier: Modifier = Modifier,
) {
    Box(modifier) {
        Text(
            text = "TODO: the user switcher goes here",
            modifier = Modifier.align(Alignment.Center)
        )
    }
}

/**
 * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap
 * anywhere on the background to flip their positions.
 */
@Composable
private fun SideBySide(
    viewModel: BouncerViewModel,
    dialogFactory: BouncerSceneDialogFactory,
    modifier: Modifier = Modifier,
) {
    val layoutDirection = LocalLayoutDirection.current
    val isLeftToRight = layoutDirection == LayoutDirection.Ltr
    val (isUserSwitcherFirst, setUserSwitcherFirst) =
        rememberSaveable(isLeftToRight) { mutableStateOf(isLeftToRight) }

    Row(
        modifier =
            modifier.pointerInput(Unit) {
                detectTapGestures(
                    onDoubleTap = { offset ->
                        // Depending on where the user double tapped, switch the elements such that
                        // the bouncer contents element is closer to the side that was double
                        // tapped.
                        setUserSwitcherFirst(offset.x > size.width / 2)
                    }
                )
            },
    ) {
        val animatedOffset by
            animateFloatAsState(
                targetValue =
                    if (isUserSwitcherFirst) {
                        // When the user switcher is first, both elements have their natural
                        // placement so they are not offset in any way.
                        0f
                    } else if (isLeftToRight) {
                        // Since the user switcher is not first, the elements have to be swapped
                        // horizontally. In the case of LTR locales, this means pushing the user
                        // switcher to the right, hence the positive number.
                        1f
                    } else {
                        // Since the user switcher is not first, the elements have to be swapped
                        // horizontally. In the case of RTL locales, this means pushing the user
                        // switcher to the left, hence the negative number.
                        -1f
                    },
                label = "offset",
            )

        UserSwitcher(
            modifier =
                Modifier.fillMaxHeight().weight(1f).graphicsLayer {
                    translationX = size.width * animatedOffset
                },
        )
        Bouncer(
            viewModel = viewModel,
            dialogFactory = dialogFactory,
            modifier =
                Modifier.fillMaxHeight().weight(1f).graphicsLayer {
                    // A negative sign is used to make sure this is offset in the direction that's
                    // opposite of the direction that the user switcher is pushed in.
                    translationX = -size.width * animatedOffset
                },
        )
    }
}

/** Arranges the bouncer contents and user switcher contents one on top of the other. */
@Composable
private fun Stacked(
    viewModel: BouncerViewModel,
    dialogFactory: BouncerSceneDialogFactory,
    modifier: Modifier = Modifier,
) {
    Column(
        modifier = modifier,
    ) {
        UserSwitcher(
            modifier = Modifier.fillMaxWidth().weight(1f),
        )
        Bouncer(
            viewModel = viewModel,
            dialogFactory = dialogFactory,
            modifier = Modifier.fillMaxWidth().weight(1f),
        )
    }
}

interface BouncerSceneDialogFactory {