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

Commit 8963f64c authored by Grace Cheng's avatar Grace Cheng Committed by Android (Google) Code Review
Browse files

Merge "Clear UDFPS BiometricPrompt a11y messages after read by talkback" into main

parents 90cead41 d0ac6337
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:accessibilityLiveRegion="assertive"
        android:importantForAccessibility="yes"
        android:importantForAccessibility="auto"
        android:clickable="false"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/rightGuideline"
+1 −1
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ android:layout_height="match_parent">
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:accessibilityLiveRegion="assertive"
        android:importantForAccessibility="yes"
        android:importantForAccessibility="auto"
        android:clickable="false"
        android:paddingHorizontal="16dp"
        android:paddingVertical="16dp"
+37 −23
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES
import android.view.accessibility.AccessibilityManager
import android.widget.Button
import android.widget.ImageView
@@ -43,7 +44,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieCompositionFactory
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.biometrics.Utils.ellipsize
import com.android.systemui.biometrics.shared.model.BiometricModalities
import com.android.systemui.biometrics.shared.model.BiometricModality
@@ -63,6 +63,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

private const val TAG = "BiometricViewBinder"

@@ -123,6 +124,19 @@ object BiometricViewBinder {
        val confirmationButton = view.requireViewById<Button>(R.id.button_confirm)
        val retryButton = view.requireViewById<Button>(R.id.button_try_again)

        // TODO(b/330788871): temporary workaround for the unsafe callbacks & legacy controllers
        val adapter =
            Spaghetti(
                view = view,
                viewModel = viewModel,
                applicationContext = view.context.applicationContext,
                applicationScope = applicationScope,
            )

        // bind to prompt
        var boundSize = false

        view.repeatWhenAttached {
            // Handles custom "Cancel Authentication" talkback action
            val cancelDelegate: AccessibilityDelegateCompat =
                object : AccessibilityDelegateCompat() {
@@ -131,10 +145,18 @@ object BiometricViewBinder {
                        info: AccessibilityNodeInfoCompat,
                    ) {
                        super.onInitializeAccessibilityNodeInfo(host, info)
                        lifecycleScope.launch {
                            // Clears UDFPS guidance hint after focus moves to cancel view
                            viewModel.onClearUdfpsGuidanceHint(
                                accessibilityManager.isTouchExplorationEnabled
                            )
                        }
                        info.addAction(
                            AccessibilityActionCompat(
                                AccessibilityNodeInfoCompat.ACTION_CLICK,
                            view.context.getString(R.string.biometric_dialog_cancel_authentication),
                                view.context.getString(
                                    R.string.biometric_dialog_cancel_authentication
                                ),
                            )
                        )
                    }
@@ -142,19 +164,6 @@ object BiometricViewBinder {
            ViewCompat.setAccessibilityDelegate(backgroundView, cancelDelegate)
            ViewCompat.setAccessibilityDelegate(cancelButton, cancelDelegate)

        // TODO(b/330788871): temporary workaround for the unsafe callbacks & legacy controllers
        val adapter =
            Spaghetti(
                view = view,
                viewModel = viewModel,
                applicationContext = view.context.applicationContext,
                applicationScope = applicationScope,
            )

        // bind to prompt
        var boundSize = false

        view.repeatWhenAttached {
            // these do not change and need to be set before any size transitions
            val modalities = viewModel.modalities.first()

@@ -404,11 +413,16 @@ object BiometricViewBinder {
                    }
                    false
                }

                launch {
                    viewModel.accessibilityHint.collect { message ->
                        if (message.isNotBlank()) {
                            udfpsGuidanceView.contentDescription = message
                        udfpsGuidanceView.importantForAccessibility =
                            if (message == null) {
                                IMPORTANT_FOR_ACCESSIBILITY_NO
                            } else {
                                IMPORTANT_FOR_ACCESSIBILITY_YES
                            }
                        udfpsGuidanceView.contentDescription = message
                    }
                }

+15 −2
Original line number Diff line number Diff line
@@ -187,10 +187,10 @@ constructor(
            }
        }

    private val _accessibilityHint = MutableSharedFlow<String>()
    private val _accessibilityHint = MutableSharedFlow<String?>()

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

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

@@ -923,6 +923,19 @@ constructor(
        return false
    }

    /** Clears the message used for UDFPS directional guidance */
    suspend fun onClearUdfpsGuidanceHint(touchExplorationEnabled: Boolean) {
        if (
            modalities.first().hasUdfps &&
                touchExplorationEnabled &&
                !isAuthenticated.first().isAuthenticated
        ) {
            // Add delay to make sure we read the guidance message before clearing it
            delay(1000)
            _accessibilityHint.emit(null)
        }
    }

    /**
     * Switch to the credential view.
     *
+8 −0
Original line number Diff line number Diff line
@@ -1482,6 +1482,14 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
        } else {
            assertThat(hint.isNullOrBlank()).isTrue()
        }

        kosmos.promptViewModel.onClearUdfpsGuidanceHint(true)

        if (testCase.modalities.hasUdfps) {
            assertThat(hint).isNull()
        } else {
            assertThat(hint.isNullOrBlank()).isTrue()
        }
    }

    @Test