Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +8 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } } Loading Loading @@ -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); } Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java +25 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) { Loading @@ -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; }); Loading @@ -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(); Loading @@ -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 Loading Loading @@ -136,6 +150,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) { Loading packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +37 −14 Original line number Diff line number Diff line Loading @@ -110,6 +110,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) Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +8 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } } Loading Loading @@ -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); } Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java +25 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) { Loading @@ -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; }); Loading @@ -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(); Loading @@ -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 Loading Loading @@ -136,6 +150,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) { Loading
packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +37 −14 Original line number Diff line number Diff line Loading @@ -110,6 +110,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) Loading Loading @@ -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 Loading Loading @@ -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 Loading