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

Commit 050eb5e4 authored by Chandru S's avatar Chandru S Committed by Android (Google) Code Review
Browse files

Merge changes I064c31b8,I30facf06 into main

* changes:
  [flexiglass] Add view+viewmodel layer support for 2-line bouncer messages
  Show unlock with fingerprint only for devices where the fingerprint sensor is not below the display
parents 819489a8 ebec01b8
Loading
Loading
Loading
Loading
+39 −17
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -71,6 +72,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.times
import com.android.compose.PlatformButton
import com.android.compose.animation.scene.ElementKey
@@ -84,7 +86,9 @@ import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerMessageViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.MessageViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
@@ -166,7 +170,7 @@ private fun StandardLayout(
                modifier = Modifier.fillMaxWidth(),
            ) {
                StatusMessage(
                    viewModel = viewModel,
                    viewModel = viewModel.message,
                    modifier = Modifier,
                )

@@ -228,7 +232,7 @@ private fun SplitLayout(
            when (authMethod) {
                is PinBouncerViewModel -> {
                    StatusMessage(
                        viewModel = viewModel,
                        viewModel = viewModel.message,
                        modifier = Modifier.align(Alignment.TopCenter),
                    )

@@ -241,7 +245,7 @@ private fun SplitLayout(
                }
                is PatternBouncerViewModel -> {
                    StatusMessage(
                        viewModel = viewModel,
                        viewModel = viewModel.message,
                        modifier = Modifier.align(Alignment.TopCenter),
                    )

@@ -280,7 +284,7 @@ private fun SplitLayout(
                        modifier = Modifier.fillMaxWidth().align(Alignment.Center),
                    ) {
                        StatusMessage(
                            viewModel = viewModel,
                            viewModel = viewModel.message,
                        )

                        OutputArea(viewModel = viewModel, modifier = Modifier.padding(top = 24.dp))
@@ -376,7 +380,7 @@ private fun BesideUserSwitcherLayout(
                    modifier = Modifier.fillMaxWidth()
                ) {
                    StatusMessage(
                        viewModel = viewModel,
                        viewModel = viewModel.message,
                    )

                    OutputArea(viewModel = viewModel, modifier = Modifier.padding(top = 24.dp))
@@ -441,7 +445,7 @@ private fun BelowUserSwitcherLayout(
                modifier = Modifier.fillMaxWidth(),
            ) {
                StatusMessage(
                    viewModel = viewModel,
                    viewModel = viewModel.message,
                )

                OutputArea(viewModel = viewModel, modifier = Modifier.padding(top = 24.dp))
@@ -548,26 +552,44 @@ private fun SceneScope.FoldableScene(

@Composable
private fun StatusMessage(
    viewModel: BouncerViewModel,
    viewModel: BouncerMessageViewModel,
    modifier: Modifier = Modifier,
) {
    val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
    val message: MessageViewModel? by viewModel.message.collectAsState()

    DisposableEffect(Unit) {
        viewModel.onShown()
        onDispose {}
    }

    Crossfade(
        targetState = message,
        label = "Bouncer message",
        animationSpec = if (message.isUpdateAnimated) tween() else snap(),
        animationSpec = if (message?.isUpdateAnimated == true) tween() else snap(),
        modifier = modifier.fillMaxWidth(),
    ) {
        Box(
            contentAlignment = Alignment.Center,
    ) { msg ->
        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
            modifier = Modifier.fillMaxWidth(),
        ) {
            msg?.let {
                Text(
                    text = it.text,
                    color = MaterialTheme.colorScheme.onSurface,
                style = MaterialTheme.typography.bodyLarge,
                    fontSize = 18.sp,
                    lineHeight = 24.sp,
                    overflow = TextOverflow.Ellipsis,
                )
                Spacer(modifier = Modifier.size(10.dp))
                Text(
                    text = it.secondaryText ?: "",
                    color = MaterialTheme.colorScheme.onSurface,
                    fontSize = 14.sp,
                    lineHeight = 20.sp,
                    overflow = TextOverflow.Ellipsis,
                    maxLines = 2
                )
            }
        }
    }
}
+1 −4
Original line number Diff line number Diff line
@@ -74,10 +74,7 @@ internal fun PasswordBouncer(
    val isImeSwitcherButtonVisible by viewModel.isImeSwitcherButtonVisible.collectAsState()
    val selectedUserId by viewModel.selectedUserId.collectAsState()

    DisposableEffect(Unit) {
        viewModel.onShown()
        onDispose { viewModel.onHidden() }
    }
    DisposableEffect(Unit) { onDispose { viewModel.onHidden() } }

    LaunchedEffect(animateFailure) {
        if (animateFailure) {
+1 −4
Original line number Diff line number Diff line
@@ -72,10 +72,7 @@ internal fun PatternBouncer(
    centerDotsVertically: Boolean,
    modifier: Modifier = Modifier,
) {
    DisposableEffect(Unit) {
        viewModel.onShown()
        onDispose { viewModel.onHidden() }
    }
    DisposableEffect(Unit) { onDispose { viewModel.onHidden() } }

    val colCount = viewModel.columnCount
    val rowCount = viewModel.rowCount
+1 −4
Original line number Diff line number Diff line
@@ -72,10 +72,7 @@ fun PinPad(
    verticalSpacing: Dp,
    modifier: Modifier = Modifier,
) {
    DisposableEffect(Unit) {
        viewModel.onShown()
        onDispose { viewModel.onHidden() }
    }
    DisposableEffect(Unit) { onDispose { viewModel.onHidden() } }

    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
    val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState()
+0 −56
Original line number Diff line number Diff line
@@ -70,34 +70,6 @@ class BouncerInteractorTest : SysuiTestCase() {
        underTest = kosmos.bouncerInteractor
    }

    @Test
    fun pinAuthMethod() =
        testScope.runTest {
            val message by collectLastValue(underTest.message)

            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Pin
            )
            runCurrent()
            underTest.clearMessage()
            assertThat(message).isNull()

            underTest.resetMessage()
            assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)

            // Wrong input.
            assertThat(underTest.authenticate(listOf(9, 8, 7)))
                .isEqualTo(AuthenticationResult.FAILED)
            assertThat(message).isEqualTo(MESSAGE_WRONG_PIN)

            underTest.resetMessage()
            assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)

            // Correct input.
            assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
                .isEqualTo(AuthenticationResult.SUCCEEDED)
        }

    @Test
    fun pinAuthMethod_sim_skipsAuthentication() =
        testScope.runTest {
@@ -146,8 +118,6 @@ class BouncerInteractorTest : SysuiTestCase() {
    @Test
    fun pinAuthMethod_tryAutoConfirm_withoutAutoConfirmPin() =
        testScope.runTest {
            val message by collectLastValue(underTest.message)

            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Pin
            )
@@ -156,7 +126,6 @@ class BouncerInteractorTest : SysuiTestCase() {
            // Incomplete input.
            assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true))
                .isEqualTo(AuthenticationResult.SKIPPED)
            assertThat(message).isNull()

            // Correct input.
            assertThat(
@@ -166,28 +135,19 @@ class BouncerInteractorTest : SysuiTestCase() {
                    )
                )
                .isEqualTo(AuthenticationResult.SKIPPED)
            assertThat(message).isNull()
        }

    @Test
    fun passwordAuthMethod() =
        testScope.runTest {
            val message by collectLastValue(underTest.message)
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Password
            )
            runCurrent()

            underTest.resetMessage()
            assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)

            // Wrong input.
            assertThat(underTest.authenticate("alohamora".toList()))
                .isEqualTo(AuthenticationResult.FAILED)
            assertThat(message).isEqualTo(MESSAGE_WRONG_PASSWORD)

            underTest.resetMessage()
            assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)

            // Too short input.
            assertThat(
@@ -201,7 +161,6 @@ class BouncerInteractorTest : SysuiTestCase() {
                    )
                )
                .isEqualTo(AuthenticationResult.SKIPPED)
            assertThat(message).isEqualTo(MESSAGE_WRONG_PASSWORD)

            // Correct input.
            assertThat(underTest.authenticate("password".toList()))
@@ -211,13 +170,10 @@ class BouncerInteractorTest : SysuiTestCase() {
    @Test
    fun patternAuthMethod() =
        testScope.runTest {
            val message by collectLastValue(underTest.message)
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Pattern
            )
            runCurrent()
            underTest.resetMessage()
            assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)

            // Wrong input.
            val wrongPattern =
@@ -231,10 +187,6 @@ class BouncerInteractorTest : SysuiTestCase() {
            assertThat(wrongPattern.size)
                .isAtLeast(kosmos.fakeAuthenticationRepository.minPatternLength)
            assertThat(underTest.authenticate(wrongPattern)).isEqualTo(AuthenticationResult.FAILED)
            assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN)

            underTest.resetMessage()
            assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)

            // Too short input.
            val tooShortPattern =
@@ -244,10 +196,6 @@ class BouncerInteractorTest : SysuiTestCase() {
                )
            assertThat(underTest.authenticate(tooShortPattern))
                .isEqualTo(AuthenticationResult.SKIPPED)
            assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN)

            underTest.resetMessage()
            assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)

            // Correct input.
            assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN))
@@ -258,7 +206,6 @@ class BouncerInteractorTest : SysuiTestCase() {
    fun lockoutStarted() =
        testScope.runTest {
            val lockoutStartedEvents by collectValues(underTest.onLockoutStarted)
            val message by collectLastValue(underTest.message)

            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Pin
@@ -272,17 +219,14 @@ class BouncerInteractorTest : SysuiTestCase() {
                    .isEqualTo(AuthenticationResult.FAILED)
                if (times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) {
                    assertThat(lockoutStartedEvents).isEmpty()
                    assertThat(message).isNotEmpty()
                }
            }
            assertThat(authenticationInteractor.lockoutEndTimestamp).isNotNull()
            assertThat(lockoutStartedEvents.size).isEqualTo(1)
            assertThat(message).isNull()

            // Advance the time to finish the lockout:
            advanceTimeBy(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS.seconds)
            assertThat(authenticationInteractor.lockoutEndTimestamp).isNull()
            assertThat(message).isNull()
            assertThat(lockoutStartedEvents.size).isEqualTo(1)

            // Trigger lockout again:
Loading