Add support for Predictive Back in CredentialPasswordViewBinder

Test: atest AuthContainerViewTest
Bug: 254450850
Change-Id: I3d71d3f595eba16b2e1cab01d915dd7842717a6e
This commit is contained in:
omarmt
2022-11-09 16:28:53 +00:00
committed by Omar Miatello
parent d7fcc948d6
commit 1417609ab3
2 changed files with 62 additions and 20 deletions

View File

@@ -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
}

View File

@@ -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