diff --git a/packages/SystemUI/res/layout/auth_container_view.xml b/packages/SystemUI/res/layout/auth_container_view.xml index d83776b74fd13..23199aacc0931 100644 --- a/packages/SystemUI/res/layout/auth_container_view.xml +++ b/packages/SystemUI/res/layout/auth_container_view.xml @@ -23,7 +23,8 @@ android:id="@+id/background" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/biometric_dialog_dim_color"/> + android:background="@color/biometric_dialog_dim_color" + android:contentDescription="@string/biometric_dialog_empty_space_description"/> Try again - Empty region, tap to cancel authentication + Tap to cancel authentication Please try again @@ -305,6 +305,8 @@ Confirmed Tap Confirm to complete + + Authenticated Touch the fingerprint sensor diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java index 52a13cc9c87f4..1d47fc520ec2a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java @@ -111,8 +111,12 @@ public class AuthBiometricFaceView extends AuthBiometricView { R.string.biometric_dialog_face_icon_description_confirmed)); } else if (lastStateIsErrorIcon && newState == STATE_IDLE) { animateOnce(R.drawable.face_dialog_error_to_idle); + mIconView.setContentDescription(mContext.getString( + R.string.biometric_dialog_face_icon_description_idle)); } else if (lastStateIsErrorIcon && newState == STATE_AUTHENTICATED) { animateOnce(R.drawable.face_dialog_dark_to_checkmark); + mIconView.setContentDescription(mContext.getString( + R.string.biometric_dialog_face_icon_description_authenticated)); } else if (newState == STATE_ERROR && lastState != STATE_ERROR) { animateOnce(R.drawable.face_dialog_dark_to_error); } else if (lastState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) { @@ -125,6 +129,8 @@ public class AuthBiometricFaceView extends AuthBiometricView { R.string.biometric_dialog_face_icon_description_authenticated)); } else if (newState == STATE_IDLE) { showStaticDrawable(R.drawable.face_dialog_idle_static); + mIconView.setContentDescription(mContext.getString( + R.string.biometric_dialog_face_icon_description_idle)); } else { Log.w(TAG, "Unhandled state: " + newState); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index 12902d5e283b3..e4d2005c88675 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -16,6 +16,8 @@ package com.android.systemui.biometrics; +import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -32,6 +34,8 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; @@ -145,6 +149,7 @@ public abstract class AuthBiometricView extends LinearLayout { private final Injector mInjector; private final Handler mHandler; + private final AccessibilityManager mAccessibilityManager; private final int mTextColorError; private final int mTextColorHint; @@ -196,15 +201,9 @@ public abstract class AuthBiometricView extends LinearLayout { */ protected abstract boolean supportsSmallDialog(); - private final Runnable mResetErrorRunnable = () -> { - updateState(getStateForAfterError()); - handleResetAfterError(); - }; + private final Runnable mResetErrorRunnable; - private final Runnable mResetHelpRunnable = () -> { - updateState(STATE_AUTHENTICATING); - handleResetAfterHelp(); - }; + private final Runnable mResetHelpRunnable; private final OnClickListener mBackgroundClickListener = (view) -> { if (mState == STATE_AUTHENTICATED) { @@ -236,6 +235,20 @@ public abstract class AuthBiometricView extends LinearLayout { mInjector = injector; mInjector.mBiometricView = this; + + mAccessibilityManager = context.getSystemService(AccessibilityManager.class); + + mResetErrorRunnable = () -> { + updateState(getStateForAfterError()); + handleResetAfterError(); + Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); + }; + + mResetHelpRunnable = () -> { + updateState(STATE_AUTHENTICATING); + handleResetAfterHelp(); + Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); + }; } public void setPanelController(AuthPanelController panelController) { @@ -333,6 +346,8 @@ public abstract class AuthBiometricView extends LinearLayout { super.onAnimationEnd(animation); mSize = newSize; mDialogSizeAnimating = false; + Utils.notifyAccessibilityContentChanged(mAccessibilityManager, + AuthBiometricView.this); } }); @@ -348,6 +363,7 @@ public abstract class AuthBiometricView extends LinearLayout { } else { Log.e(TAG, "Unknown transition from: " + mSize + " to: " + newSize); } + Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); } public void updateState(@BiometricState int newState) { @@ -369,6 +385,8 @@ public abstract class AuthBiometricView extends LinearLayout { mNegativeButton.setVisibility(View.GONE); mIndicatorView.setVisibility(View.INVISIBLE); } + announceForAccessibility(getResources() + .getString(R.string.biometric_dialog_authenticated)); mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED), getDelayAfterAuthenticatedDurationMs()); break; @@ -395,6 +413,7 @@ public abstract class AuthBiometricView extends LinearLayout { break; } + Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); mState = newState; } @@ -461,6 +480,8 @@ public abstract class AuthBiometricView extends LinearLayout { } else { view.setText(string); } + + Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); } private void setText(TextView view, String string) { @@ -479,6 +500,8 @@ public abstract class AuthBiometricView extends LinearLayout { mIndicatorView.setTextColor(mTextColorError); mIndicatorView.setVisibility(View.VISIBLE); mHandler.postDelayed(resetMessageRunnable, BiometricPrompt.HIDE_DIALOG_DELAY); + + Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); } @Override @@ -517,6 +540,7 @@ public abstract class AuthBiometricView extends LinearLayout { updateState(STATE_AUTHENTICATING); mCallback.onAction(Callback.ACTION_BUTTON_TRY_AGAIN); mTryAgainButton.setVisibility(View.GONE); + Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); }); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java index edd8089c35f94..e00cf6abafaa0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java @@ -16,8 +16,14 @@ package com.android.systemui.biometrics; +import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE; + import android.content.Context; import android.util.DisplayMetrics; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; public class Utils { static float dpToPixels(Context context, float dp) { @@ -29,4 +35,15 @@ public class Utils { return pixels / ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT); } + + static void notifyAccessibilityContentChanged(AccessibilityManager am, ViewGroup view) { + if (!am.isEnabled()) { + return; + } + AccessibilityEvent event = AccessibilityEvent.obtain(); + event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); + event.setContentChangeTypes(CONTENT_CHANGE_TYPE_SUBTREE); + view.sendAccessibilityEventUnchecked(event); + view.notifySubtreeAccessibilityStateChanged(view, view, CONTENT_CHANGE_TYPE_SUBTREE); + } }