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

Commit 31418bed authored by Andre Le's avatar Andre Le Committed by Android (Google) Code Review
Browse files

Merge "Flexiglass: Add border for keyguard pin input when keyboard is supported" into main

parents e2f0b930 adc636ea
Loading
Loading
Loading
Loading
+18 −2
Original line number Diff line number Diff line
@@ -31,14 +31,17 @@ import androidx.compose.animation.graphics.res.animatedVectorResource
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -68,6 +71,7 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.PlatformOutlinedButton
import com.android.compose.animation.Easings
import com.android.compose.modifiers.thenIf
import com.android.keyguard.PinShapeAdapter
import com.android.systemui.bouncer.ui.viewmodel.EntryToken.Digit
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
@@ -87,6 +91,18 @@ fun PinInputDisplay(viewModel: PinBouncerViewModel, modifier: Modifier = Modifie
    val hintedPinLength: Int? by viewModel.hintedPinLength.collectAsStateWithLifecycle()
    val shapeAnimations = rememberShapeAnimations(viewModel.pinShapes)

    val isPinDisplayBorderVisible by
        viewModel.isPinDisplayBorderVisible.collectAsStateWithLifecycle(initialValue = false)
    val borderColor = colorResource(R.color.bouncer_password_focus_color)
    val pinInputHeight = dimensionResource(id = R.dimen.keyguard_password_field_height)
    val pinInputWidth = dimensionResource(id = R.dimen.keyguard_password_field_width)
    val pinInputModifier =
        modifier.thenIf(isPinDisplayBorderVisible) {
            Modifier.border(width = 3.dp, color = borderColor, shape = RoundedCornerShape(16.dp))
                .height(pinInputHeight)
                .width(pinInputWidth)
        }

    // The display comes in two different flavors:
    // 1) hinting: shows a circle (◦) per expected pin input, and dot (●) per entered digit.
    //    This has a fixed width, and uses two distinct types of AVDs to animate the addition and
@@ -99,8 +115,8 @@ fun PinInputDisplay(viewModel: PinBouncerViewModel, modifier: Modifier = Modifie
    // unifying into a single, more complex implementation.

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

+38 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.bouncer.ui.viewmodel

import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.KeyEvent.KEYCODE_0
import android.view.KeyEvent.KEYCODE_4
@@ -35,6 +36,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.bouncer.data.repository.fakeSimBouncerRepository
import com.android.systemui.haptics.msdl.bouncerHapticPlayer
import com.android.systemui.haptics.msdl.fakeMSDLPlayer
import com.android.systemui.keyboard.data.repository.keyboardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runCurrent
@@ -503,6 +505,42 @@ class PinBouncerViewModelTest : SysuiTestCase() {
            assertThat(authResult).isTrue()
        }

    @Test
    @EnableFlags(Flags.FLAG_PIN_INPUT_FIELD_STYLED_FOCUS_STATE)
    fun inputFieldStyledEnabled_onKeyboardConnectedTrue_isPinDisplayBorderVisibleTrue() =
        kosmos.runTest {
            keyboardRepository.setIsAnyKeyboardConnected(true)
            val isPinDisplayBorderVisible by collectLastValue(underTest.isPinDisplayBorderVisible)
            assertThat(isPinDisplayBorderVisible).isTrue()
        }

    @Test
    @EnableFlags(Flags.FLAG_PIN_INPUT_FIELD_STYLED_FOCUS_STATE)
    fun inputFieldStyledEnabled_onKeyboardConnectedFalse_isPinDisplayBorderVisibleFalse() =
        kosmos.runTest {
            keyboardRepository.setIsAnyKeyboardConnected(false)
            val isPinDisplayBorderVisible by collectLastValue(underTest.isPinDisplayBorderVisible)
            assertThat(isPinDisplayBorderVisible).isFalse()
        }

    @Test
    @DisableFlags(Flags.FLAG_PIN_INPUT_FIELD_STYLED_FOCUS_STATE)
    fun inputFieldStyledDisabled_onKeyboardConnectedTrue_isPinDisplayBorderVisibleFalse() =
        kosmos.runTest {
            keyboardRepository.setIsAnyKeyboardConnected(true)
            val isPinDisplayBorderVisible by collectLastValue(underTest.isPinDisplayBorderVisible)
            assertThat(isPinDisplayBorderVisible).isFalse()
        }

    @Test
    @DisableFlags(Flags.FLAG_PIN_INPUT_FIELD_STYLED_FOCUS_STATE)
    fun inputFieldStyledDisabled_onKeyboardConnectedFalse_isPinDisplayBorderVisibleFalse() =
        kosmos.runTest {
            keyboardRepository.setIsAnyKeyboardConnected(false)
            val isPinDisplayBorderVisible by collectLastValue(underTest.isPinDisplayBorderVisible)
            assertThat(isPinDisplayBorderVisible).isFalse()
        }

    @Test
    @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
    fun onDigiButtonDown_deliversKeyStandardToken() =
+10 −0
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.KeyEventType
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.PinShapeAdapter
import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor
import com.android.systemui.Flags
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
@@ -40,6 +42,7 @@ import dagger.assisted.AssistedInject
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -55,6 +58,7 @@ constructor(
    applicationContext: Context,
    interactor: BouncerInteractor,
    private val simBouncerInteractor: SimBouncerInteractor,
    keyguardKeyboardInteractor: KeyguardKeyboardInteractor,
    @Assisted bouncerHapticPlayer: BouncerHapticPlayer,
    @Assisted isInputEnabled: StateFlow<Boolean>,
    @Assisted private val onIntentionalUserInput: () -> Unit,
@@ -74,6 +78,12 @@ constructor(
    val isSimAreaVisible = authenticationMethod == AuthenticationMethodModel.Sim
    val isLockedEsim: StateFlow<Boolean?> = simBouncerInteractor.isLockedEsim
    val errorDialogMessage: StateFlow<String?> = simBouncerInteractor.errorDialogMessage

    val isPinDisplayBorderVisible: Flow<Boolean> =
        keyguardKeyboardInteractor.isAnyKeyboardConnected.map { isAnyKeyboardConnected ->
            isAnyKeyboardConnected && Flags.pinInputFieldStyledFocusState()
        }

    val isSimUnlockingDialogVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
    val pinShapes = PinShapeAdapter(applicationContext)
    private val mutablePinInput = MutableStateFlow(PinInputViewModel.empty())
+23 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.keyguard.domain.interactor

import com.android.systemui.keyboard.data.repository.keyboardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture

val Kosmos.keyguardKeyboardInteractor by Fixture { KeyguardKeyboardInteractor(keyboardRepository) }
+2 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.bouncer.ui.viewmodel

import android.app.admin.devicePolicyManager
import android.content.applicationContext
import com.android.keyguard.domain.interactor.keyguardKeyboardInteractor
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
@@ -83,6 +84,7 @@ val Kosmos.pinBouncerViewModelFactory by Fixture {
                applicationContext = applicationContext,
                interactor = bouncerInteractor,
                simBouncerInteractor = simBouncerInteractor,
                keyguardKeyboardInteractor = keyguardKeyboardInteractor,
                isInputEnabled = isInputEnabled,
                onIntentionalUserInput = onIntentionalUserInput,
                authenticationMethod = authenticationMethod,