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

Commit 690c782e authored by Alejandro Nijamkin's avatar Alejandro Nijamkin Committed by Automerger Merge Worker
Browse files

[flexiglass] Bouncer throttling - composables. am: 33ddd9ab

parents 082e558b 33ddd9ab
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.scene.ui.composable

import android.content.Context
import com.android.systemui.bouncer.ui.composable.BouncerScene
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.dagger.SysUISingleton
@@ -28,6 +29,7 @@ import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerNames
import com.android.systemui.shade.ui.composable.ShadeScene
import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
import com.android.systemui.statusbar.phone.SystemUIDialog
import dagger.Module
import dagger.Provides
import javax.inject.Named
@@ -57,6 +59,7 @@ object SceneModule {
    @SysUISingleton
    @Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
    fun bouncerScene(
        @Application context: Context,
        viewModelFactory: BouncerViewModel.Factory,
    ): BouncerScene {
        return BouncerScene(
@@ -64,6 +67,7 @@ object SceneModule {
                viewModelFactory.create(
                    containerName = SceneContainerNames.SYSTEM_UI_DEFAULT,
                ),
            dialogFactory = { SystemUIDialog(context) },
        )
    }

+42 −4
Original line number Diff line number Diff line
@@ -14,9 +14,16 @@
 * 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.snap
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -26,15 +33,20 @@ import androidx.compose.foundation.layout.fillMaxSize
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.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.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.android.systemui.R
import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
@@ -51,6 +63,7 @@ import kotlinx.coroutines.flow.asStateFlow
/** The bouncer scene displays authentication challenges like PIN, password, or pattern. */
class BouncerScene(
    private val viewModel: BouncerViewModel,
    private val dialogFactory: () -> AlertDialog,
) : ComposableScene {
    override val key = SceneKey.Bouncer

@@ -68,16 +81,19 @@ class BouncerScene(
    override fun Content(
        containerName: String,
        modifier: Modifier,
    ) = BouncerScene(viewModel, modifier)
    ) = BouncerScene(viewModel, dialogFactory, modifier)
}

@Composable
private fun BouncerScene(
    viewModel: BouncerViewModel,
    dialogFactory: () -> AlertDialog,
    modifier: Modifier = Modifier,
) {
    val message: String by viewModel.message.collectAsState()
    val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
    val authMethodViewModel: AuthMethodBouncerViewModel? by viewModel.authMethod.collectAsState()
    val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
    var dialog: Dialog? by remember { mutableStateOf(null) }

    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
@@ -88,9 +104,10 @@ private fun BouncerScene(
        Crossfade(
            targetState = message,
            label = "Bouncer message",
        ) {
            animationSpec = if (message.isUpdateAnimated) tween() else snap(),
        ) { message ->
            Text(
                text = it,
                text = message.text,
                color = MaterialTheme.colorScheme.onSurface,
                style = MaterialTheme.typography.bodyLarge,
            )
@@ -132,5 +149,26 @@ private fun BouncerScene(
                style = MaterialTheme.typography.bodyMedium,
            )
        }

        if (dialogMessage != null) {
            if (dialog == null) {
                dialog =
                    dialogFactory().apply {
                        setMessage(dialogMessage)
                        setButton(
                            DialogInterface.BUTTON_NEUTRAL,
                            context.getString(R.string.ok),
                        ) { _, _ ->
                            viewModel.onThrottlingDialogDismissed()
                        }
                        setCancelable(false)
                        setCanceledOnTouchOutside(false)
                        show()
                    }
            }
        } else {
            dialog?.dismiss()
            dialog = null
        }
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ internal fun PasswordBouncer(
) {
    val focusRequester = remember { FocusRequester() }
    val password: String by viewModel.password.collectAsState()
    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()

    LaunchedEffect(Unit) {
        // When the UI comes up, request focus on the TextField to bring up the software keyboard.
@@ -71,6 +72,7 @@ internal fun PasswordBouncer(
        TextField(
            value = password,
            onValueChange = viewModel::onPasswordInputChanged,
            enabled = isInputEnabled,
            visualTransformation = PasswordVisualTransformation(),
            singleLine = true,
            textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
+50 −28
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import androidx.compose.ui.unit.dp
import com.android.internal.R
import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PatternDotViewModel
import com.android.systemui.compose.modifiers.thenIf
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sqrt
@@ -82,6 +83,8 @@ internal fun PatternBouncer(
    val currentDot: PatternDotViewModel? by viewModel.currentDot.collectAsState()
    // The dots selected so far, if the user is currently dragging.
    val selectedDots: List<PatternDotViewModel> by viewModel.selectedDots.collectAsState()
    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
    val isAnimationEnabled: Boolean by viewModel.isPatternVisible.collectAsState()

    // Map of animatables for the scale of each dot, keyed by dot.
    val dotScalingAnimatables = remember(dots) { dots.associateWith { Animatable(1f) } }
@@ -96,16 +99,24 @@ internal fun PatternBouncer(
    val view = LocalView.current

    // When the current dot is changed, we need to update our animations.
    LaunchedEffect(currentDot) {
    LaunchedEffect(currentDot, isAnimationEnabled) {
        view.performHapticFeedback(
            HapticFeedbackConstants.VIRTUAL_KEY,
            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING,
        )

        // Make sure that the current dot is scaled up while the other dots are scaled back down.
        if (!isAnimationEnabled) {
            return@LaunchedEffect
        }

        // Make sure that the current dot is scaled up while the other dots are scaled back
        // down.
        dotScalingAnimatables.entries.forEach { (dot, animatable) ->
            val isSelected = dot == currentDot
            launch {
            // Launch using the longer-lived scope because we want these animations to proceed to
            // completion even if the LaunchedEffect is canceled because its key objects have
            // changed.
            scope.launch {
                animatable.animateTo(if (isSelected) 2f else 1f)
                if (isSelected) {
                    animatable.animateTo(1f)
@@ -116,14 +127,18 @@ internal fun PatternBouncer(
        selectedDots.forEach { dot ->
            lineFadeOutAnimatables[dot]?.let { line ->
                if (!line.isRunning) {
                    // Launch using the longer-lived scope because we want these animations to
                    // proceed to completion even if the LaunchedEffect is canceled because its key
                    // objects have changed.
                    scope.launch {
                        if (dot == currentDot) {
                            // Reset the fade-out animation for the current dot. When the current
                            // dot is switched, this entire code block runs again for the newly
                            // selected dot.
                            // Reset the fade-out animation for the current dot. When the
                            // current dot is switched, this entire code block runs again for
                            // the newly selected dot.
                            line.snapTo(1f)
                        } else {
                            // For all non-current dots, make sure that the lines are fading out.
                            // For all non-current dots, make sure that the lines are fading
                            // out.
                            line.animateTo(
                                targetValue = 0f,
                                animationSpec =
@@ -148,7 +163,8 @@ internal fun PatternBouncer(
            // when it leaves the bounds of the dot grid.
            .clipToBounds()
            .onSizeChanged { containerSize = it }
            .pointerInput(Unit) {
            .thenIf(isInputEnabled) {
                Modifier.pointerInput(Unit) {
                    detectDragGestures(
                        onDragStart = { start ->
                            inputPosition = start
@@ -156,9 +172,14 @@ internal fun PatternBouncer(
                        },
                        onDragEnd = {
                            inputPosition = null
                            if (isAnimationEnabled) {
                                lineFadeOutAnimatables.values.forEach { animatable ->
                                    // Launch using the longer-lived scope because we want these
                                    // animations to proceed to completion even if the surrounding
                                    // scope is canceled.
                                    scope.launch { animatable.animateTo(1f) }
                                }
                            }
                            viewModel.onDragEnd()
                        },
                    ) { change, _ ->
@@ -171,6 +192,7 @@ internal fun PatternBouncer(
                        )
                    }
                }
            }
    ) {
        // Draw lines between dots.
        selectedDots.forEachIndexed { index, dot ->
+19 −10
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ 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.compose.modifiers.thenIf
import kotlin.math.max

@Composable
@@ -75,6 +76,7 @@ internal fun PinBouncer(

    // The length of the PIN input received so far, so we know how many dots to render.
    val pinLength: Pair<Int, Int> by viewModel.pinLengths.collectAsState()
    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()

    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
@@ -116,6 +118,7 @@ internal fun PinBouncer(
                val digit = index + 1
                PinButton(
                    onClicked = { viewModel.onPinButtonClicked(digit) },
                    isEnabled = isInputEnabled,
                ) { contentColor ->
                    PinDigit(digit, contentColor)
                }
@@ -124,6 +127,7 @@ internal fun PinBouncer(
            PinButton(
                onClicked = { viewModel.onBackspaceButtonClicked() },
                onLongPressed = { viewModel.onBackspaceButtonLongPressed() },
                isEnabled = isInputEnabled,
                isHighlighted = true,
            ) { contentColor ->
                PinIcon(
@@ -138,6 +142,7 @@ internal fun PinBouncer(

            PinButton(
                onClicked = { viewModel.onPinButtonClicked(0) },
                isEnabled = isInputEnabled,
            ) { contentColor ->
                PinDigit(0, contentColor)
            }
@@ -145,6 +150,7 @@ internal fun PinBouncer(
            PinButton(
                onClicked = { viewModel.onAuthenticateButtonClicked() },
                isHighlighted = true,
                isEnabled = isInputEnabled,
            ) { contentColor ->
                PinIcon(
                    Icon.Resource(
@@ -187,6 +193,7 @@ private fun PinIcon(
@Composable
private fun PinButton(
    onClicked: () -> Unit,
    isEnabled: Boolean,
    modifier: Modifier = Modifier,
    onLongPressed: (() -> Unit)? = null,
    isHighlighted: Boolean = false,
@@ -228,7 +235,8 @@ private fun PinButton(
                        cornerRadius = CornerRadius(cornerRadius.toPx()),
                    )
                }
                .pointerInput(Unit) {
                .thenIf(isEnabled) {
                    Modifier.pointerInput(Unit) {
                        detectTapGestures(
                            onPress = {
                                isPressed = true
@@ -238,6 +246,7 @@ private fun PinButton(
                            onTap = { onClicked() },
                            onLongPress = onLongPressed?.let { { onLongPressed() } },
                        )
                    }
                },
    ) {
        content(contentColor)
Loading