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

Commit 1417609a authored by omarmt's avatar omarmt Committed by Omar Miatello
Browse files

Add support for Predictive Back in CredentialPasswordViewBinder

Test: atest AuthContainerViewTest
Bug: 254450850
Change-Id: I3d71d3f595eba16b2e1cab01d915dd7842717a6e
parent d7fcc948
Loading
Loading
Loading
Loading
+25 −6
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@ import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.ImeAwareEditText
import android.widget.TextView
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.R
@@ -13,7 +15,7 @@ import com.android.systemui.biometrics.ui.CredentialPasswordView
import com.android.systemui.biometrics.ui.CredentialView
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.launch

/** Sub-binder for the [CredentialPasswordView]. */
@@ -29,6 +31,8 @@ object CredentialPasswordViewBinder {

        val passwordField: ImeAwareEditText = view.requireViewById(R.id.lockPassword)

        val onBackInvokedCallback = OnBackInvokedCallback { host.onCredentialAborted() }

        view.repeatWhenAttached {
            passwordField.requestFocus()
            passwordField.scheduleShowSoftInput()
@@ -43,9 +47,7 @@ object CredentialPasswordViewBinder {
                                launch { viewModel.checkCredential(text, header) }
                            }
                        )
                        passwordField.setOnKeyListener(
                            OnBackButtonListener { host.onCredentialAborted() }
                        )
                        passwordField.setOnKeyListener(OnBackButtonListener(onBackInvokedCallback))
                    }
                }

@@ -66,18 +68,35 @@ object CredentialPasswordViewBinder {
                        }
                    }
                }

                val onBackInvokedDispatcher = view.findOnBackInvokedDispatcher()
                if (onBackInvokedDispatcher != null) {
                    launch {
                            onBackInvokedDispatcher.registerOnBackInvokedCallback(
                                OnBackInvokedDispatcher.PRIORITY_DEFAULT,
                                onBackInvokedCallback
                            )
                            awaitCancellation()
                        }
                        .invokeOnCompletion {
                            onBackInvokedDispatcher.unregisterOnBackInvokedCallback(
                                onBackInvokedCallback
                            )
                        }
                }
            }
        }
    }
}

private class OnBackButtonListener(private val onBack: () -> Unit) : View.OnKeyListener {
private class OnBackButtonListener(private val onBackInvokedCallback: OnBackInvokedCallback) :
    View.OnKeyListener {
    override fun onKey(v: View, keyCode: Int, event: KeyEvent): Boolean {
        if (keyCode != KeyEvent.KEYCODE_BACK) {
            return false
        }
        if (event.action == KeyEvent.ACTION_UP) {
            onBack()
            onBackInvokedCallback.onBackInvoked()
        }
        return true
    }
+37 −14
Original line number Diff line number Diff line
@@ -124,6 +124,21 @@ class AuthContainerViewTest : SysuiTestCase() {
        assertThat(root.isAttachedToWindow).isFalse()
    }

    @Test
    fun testCredentialPasswordDismissesOnBack() {
        val container = initializeCredentialPasswordContainer(addToView = true)
        assertThat(container.parent).isNotNull()
        val root = container.rootView

        // Simulate back invocation
        container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK))
        container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK))
        waitForIdleSync()

        assertThat(container.parent).isNull()
        assertThat(root.isAttachedToWindow).isFalse()
    }

    @Test
    fun testIgnoresAnimatedInWhenDismissed() {
        val container = initializeFingerprintContainer(addToView = false)
@@ -369,20 +384,7 @@ class AuthContainerViewTest : SysuiTestCase() {

    @Test
    fun testCredentialUI_disablesClickingOnBackground() {
        whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(20)
        whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20))).thenReturn(
            DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
        )

        // In the credential view, clicking on the background (to cancel authentication) is not
        // valid. Thus, the listener should be null, and it should not be in the accessibility
        // hierarchy.
        val container = initializeFingerprintContainer(
            authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
        )
        waitForIdleSync()

        assertThat(container.hasCredentialPasswordView()).isTrue()
        val container = initializeCredentialPasswordContainer()
        assertThat(container.hasBiometricPrompt()).isFalse()
        assertThat(
            container.findViewById<View>(R.id.background)?.isImportantForAccessibility
@@ -442,6 +444,27 @@ class AuthContainerViewTest : SysuiTestCase() {
        verify(callback).onTryAgainPressed(authContainer?.requestId ?: 0L)
    }

    private fun initializeCredentialPasswordContainer(
            addToView: Boolean = true,
    ): TestAuthContainerView {
        whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(20)
        whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20))).thenReturn(
            DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
        )

        // In the credential view, clicking on the background (to cancel authentication) is not
        // valid. Thus, the listener should be null, and it should not be in the accessibility
        // hierarchy.
        val container = initializeFingerprintContainer(
                authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
                addToView = addToView,
        )
        waitForIdleSync()

        assertThat(container.hasCredentialPasswordView()).isTrue()
        return container
    }

    private fun initializeFingerprintContainer(
        authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
        addToView: Boolean = true