diff options
3 files changed, 70 insertions, 23 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 94f71580901c..68e1f72d042a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -129,6 +129,7 @@ public class AuthContainerView extends LinearLayout private final float mTranslationY; @VisibleForTesting @ContainerState int mContainerState = STATE_UNKNOWN; private final Set<Integer> mFailedModalities = new HashSet<Integer>(); + private OnBackInvokedDispatcher mOnBackInvokedDispatcher; private final OnBackInvokedCallback mBackCallback = this::onBackInvoked; private final @Background DelayableExecutor mBackgroundExecutor; @@ -497,9 +498,9 @@ public class AuthContainerView extends LinearLayout .start(); }); } - OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher(); - if (dispatcher != null) { - dispatcher.registerOnBackInvokedCallback( + mOnBackInvokedDispatcher = findOnBackInvokedDispatcher(); + if (mOnBackInvokedDispatcher != null) { + mOnBackInvokedDispatcher.registerOnBackInvokedCallback( OnBackInvokedDispatcher.PRIORITY_DEFAULT, mBackCallback); } } @@ -600,11 +601,11 @@ public class AuthContainerView extends LinearLayout @Override public void onDetachedFromWindow() { - OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher(); - if (dispatcher != null) { - findOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mBackCallback); - } super.onDetachedFromWindow(); + if (mOnBackInvokedDispatcher != null) { + mOnBackInvokedDispatcher.unregisterOnBackInvokedCallback(mBackCallback); + mOnBackInvokedDispatcher = null; + } mWakefulnessLifecycle.removeObserver(this); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java index 76cd3f4c4f1d..e43c0b9987ab 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java @@ -35,6 +35,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 com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternUtils; @@ -58,6 +60,8 @@ public class AuthCredentialPasswordView extends AuthCredentialView private ViewGroup mAuthCredentialHeader; private ViewGroup mAuthCredentialInput; private int mBottomInset = 0; + private OnBackInvokedDispatcher mOnBackInvokedDispatcher; + private final OnBackInvokedCallback mBackCallback = this::onBackInvoked; public AuthCredentialPasswordView(Context context, AttributeSet attrs) { @@ -79,8 +83,7 @@ public class AuthCredentialPasswordView extends AuthCredentialView return false; } if (event.getAction() == KeyEvent.ACTION_UP) { - mContainerView.sendEarlyUserCanceled(); - mContainerView.animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); + onBackInvoked(); } return true; }); @@ -88,6 +91,11 @@ public class AuthCredentialPasswordView extends AuthCredentialView setOnApplyWindowInsetsListener(this); } + private void onBackInvoked() { + mContainerView.sendEarlyUserCanceled(); + mContainerView.animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -100,6 +108,12 @@ public class AuthCredentialPasswordView extends AuthCredentialView mPasswordField.requestFocus(); mPasswordField.scheduleShowSoftInput(); + + mOnBackInvokedDispatcher = findOnBackInvokedDispatcher(); + if (mOnBackInvokedDispatcher != null) { + mOnBackInvokedDispatcher.registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mBackCallback); + } } @Override @@ -137,6 +151,15 @@ public class AuthCredentialPasswordView extends AuthCredentialView } @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mOnBackInvokedDispatcher != null) { + mOnBackInvokedDispatcher.unregisterOnBackInvokedCallback(mBackCallback); + mOnBackInvokedDispatcher = null; + } + } + + @Override protected void onCredentialVerified(@NonNull VerifyCredentialResponse response, int timeoutMs) { super.onCredentialVerified(response, timeoutMs); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index eaef159e9020..5e6acd29e520 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -111,6 +111,21 @@ class AuthContainerViewTest : SysuiTestCase() { } @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) container.dismissFromSystemServer() @@ -355,20 +370,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 @@ -428,6 +430,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 |