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

Commit ce6590eb authored by Austin Delgado's avatar Austin Delgado Committed by Android (Google) Code Review
Browse files

Merge "Add UDFPS talkback directional guidance for BP" into main

parents bc6ca765 cb46bcef
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -37,6 +37,10 @@ data class BiometricModalities(
    val hasSfps: Boolean
        get() = hasFingerprint && fingerprintProperties!!.isAnySidefpsType

    /** If UDFPS authentication is available. */
    val hasUdfps: Boolean
        get() = hasFingerprint && fingerprintProperties!!.isAnyUdfpsType

    /** If fingerprint authentication is available (and [faceProperties] is non-null). */
    val hasFace: Boolean
        get() = faceProperties != null
+16 −0
Original line number Diff line number Diff line
@@ -376,6 +376,22 @@ object BiometricViewBinder {
                    }
                }

                // Talkback directional guidance
                backgroundView.setOnHoverListener { _, event ->
                    launch {
                        viewModel.onAnnounceAccessibilityHint(
                            event,
                            accessibilityManager.isTouchExplorationEnabled
                        )
                    }
                    false
                }
                launch {
                    viewModel.accessibilityHint.collect { message ->
                        if (message.isNotBlank()) view.announceForAccessibility(message)
                    }
                }

                // Play haptics
                launch {
                    viewModel.hapticsToPlay.collect { hapticFeedbackConstant ->
+47 −1
Original line number Diff line number Diff line
@@ -21,9 +21,12 @@ import android.hardware.biometrics.BiometricPrompt
import android.util.Log
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import com.android.systemui.Flags.bpTalkback
import com.android.systemui.biometrics.UdfpsUtils
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.shared.model.BiometricModalities
import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.DisplayRotation
@@ -35,7 +38,9 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -49,7 +54,9 @@ class PromptViewModel
constructor(
    displayStateInteractor: DisplayStateInteractor,
    promptSelectorInteractor: PromptSelectorInteractor,
    @Application context: Context,
    @Application private val context: Context,
    private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
    private val udfpsUtils: UdfpsUtils
) {
    /** The set of modalities available for this prompt */
    val modalities: Flow<BiometricModalities> =
@@ -69,6 +76,11 @@ constructor(
    val faceIconHeight: Int =
        context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size)

    private val _accessibilityHint = MutableSharedFlow<String>()

    /** Hint for talkback directional guidance */
    val accessibilityHint: Flow<String> = _accessibilityHint.asSharedFlow()

    private val _isAuthenticating: MutableStateFlow<Boolean> = MutableStateFlow(false)

    /** If the user is currently authenticating (i.e. at least one biometric is scanning). */
@@ -516,6 +528,40 @@ constructor(
        return false
    }

    /** Sets the message used for UDFPS directional guidance */
    suspend fun onAnnounceAccessibilityHint(
        event: MotionEvent,
        touchExplorationEnabled: Boolean,
    ): Boolean {
        if (bpTalkback() && modalities.first().hasUdfps && touchExplorationEnabled) {
            // TODO(b/315184924): Remove uses of UdfpsUtils
            val scaledTouch =
                udfpsUtils.getTouchInNativeCoordinates(
                    event.getPointerId(0),
                    event,
                    udfpsOverlayInteractor.udfpsOverlayParams.value
                )
            if (
                !udfpsUtils.isWithinSensorArea(
                    event.getPointerId(0),
                    event,
                    udfpsOverlayInteractor.udfpsOverlayParams.value
                )
            ) {
                _accessibilityHint.emit(
                    udfpsUtils.onTouchOutsideOfSensorArea(
                        touchExplorationEnabled,
                        context,
                        scaledTouch.x,
                        scaledTouch.y,
                        udfpsOverlayInteractor.udfpsOverlayParams.value
                    )
                )
            }
        }
        return false
    }

    /**
     * Switch to the credential view.
     *
+17 −0
Original line number Diff line number Diff line
@@ -47,12 +47,14 @@ import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorI
import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.events.ANIMATING_OUT
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -101,6 +103,12 @@ open class AuthContainerViewTest : SysuiTestCase() {
    lateinit var interactionJankMonitor: InteractionJankMonitor
    @Mock
    lateinit var vibrator: VibratorHelper
    @Mock
    lateinit var udfpsUtils: UdfpsUtils
    @Mock
    lateinit var authController: AuthController
    @Mock
    lateinit var selectedUserInteractor: SelectedUserInteractor

    private val testScope = TestScope(StandardTestDispatcher())
    private val fakeExecutor = FakeExecutor(FakeSystemClock())
@@ -123,6 +131,7 @@ open class AuthContainerViewTest : SysuiTestCase() {

    private lateinit var displayRepository: FakeDisplayRepository
    private lateinit var displayStateInteractor: DisplayStateInteractor
    private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor

    private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)

@@ -140,6 +149,12 @@ open class AuthContainerViewTest : SysuiTestCase() {
                    displayStateRepository,
                    displayRepository,
            )
        udfpsOverlayInteractor =
                UdfpsOverlayInteractor(
                        authController,
                        selectedUserInteractor,
                        testScope.backgroundScope,
                )
    }

    @After
@@ -532,6 +547,8 @@ open class AuthContainerViewTest : SysuiTestCase() {
            displayStateInteractor,
            promptSelectorInteractor,
            context,
            udfpsOverlayInteractor,
            udfpsUtils
        ),
        { credentialViewModel },
        Handler(TestableLooper.get(this).looper),
+41 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.biometrics.shared.model

import android.hardware.fingerprint.FingerprintSensorProperties
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.faceSensorPropertiesInternal
@@ -34,6 +35,46 @@ class BiometricModalitiesTest : SysuiTestCase() {
        assertThat(BiometricModalities().isEmpty).isTrue()
    }

    @Test
    fun hasUdfps() {
        with(
            BiometricModalities(
                fingerprintProperties = fingerprintSensorPropertiesInternal(
                    sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
                ).first(),
            )
        ) {
            assertThat(isEmpty).isFalse()
            assertThat(hasUdfps).isTrue()
            assertThat(hasSfps).isFalse()
            assertThat(hasFace).isFalse()
            assertThat(hasFaceOnly).isFalse()
            assertThat(hasFingerprint).isTrue()
            assertThat(hasFingerprintOnly).isTrue()
            assertThat(hasFaceAndFingerprint).isFalse()
        }
    }

    @Test
    fun hasSfps() {
        with(
            BiometricModalities(
                fingerprintProperties = fingerprintSensorPropertiesInternal(
                    sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
                ).first(),
            )
        ) {
            assertThat(isEmpty).isFalse()
            assertThat(hasUdfps).isFalse()
            assertThat(hasSfps).isTrue()
            assertThat(hasFace).isFalse()
            assertThat(hasFaceOnly).isFalse()
            assertThat(hasFingerprint).isTrue()
            assertThat(hasFingerprintOnly).isTrue()
            assertThat(hasFaceAndFingerprint).isFalse()
        }
    }

    @Test
    fun fingerprintOnly() {
        with(
Loading