3/n: Add FaceDialogView and "confirmation" plumbing

This change adds plumbing from <Biometric>Service to SystemUI for requiring
user confirmation (or not). This change also fixes some layout bugs
that were hard to notice.

Bug: 111461540
Fixes: 113130114
Fixes: 116135579

Test: With confirmation, crypto operation does not work until token
      is added to Keystore
Test: Without confirmation, crypto operation works when biometric is
      authenticated
Test: BiometricPromptDemo works, UI elements are all correct
Test: Talkback messages are correct

Change-Id: I2c05577699a29c09777cae4db6c7334c1e6bc179
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_bg.xml b/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/fingerprint_dialog_bg.xml
rename to packages/SystemUI/res/drawable/biometric_dialog_bg.xml
diff --git a/packages/SystemUI/res/drawable/face_dialog_icon.xml b/packages/SystemUI/res/drawable/face_dialog_icon.xml
new file mode 100644
index 0000000..6d28b5a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/face_dialog_icon.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path android:fillColor="#000" android:pathData="M9,11.75A1.25,1.25 0 0,0 7.75,13A1.25,1.25 0 0,0 9,14.25A1.25,1.25 0 0,0 10.25,13A1.25,1.25 0 0,0 9,11.75M15,11.75A1.25,1.25 0 0,0 13.75,13A1.25,1.25 0 0,0 15,14.25A1.25,1.25 0 0,0 16.25,13A1.25,1.25 0 0,0 15,11.75M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,11.71 4,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/fingerprint_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml
similarity index 85%
rename from packages/SystemUI/res/layout/fingerprint_dialog.xml
rename to packages/SystemUI/res/layout/biometric_dialog.xml
index 06d1e0b..0417e2e 100644
--- a/packages/SystemUI/res/layout/fingerprint_dialog.xml
+++ b/packages/SystemUI/res/layout/biometric_dialog.xml
@@ -47,7 +47,7 @@
             android:layout_height="wrap_content"
             android:orientation="vertical"
             android:elevation="2dp"
-            android:background="@drawable/fingerprint_dialog_bg">
+            android:background="@drawable/biometric_dialog_bg">
 
             <TextView
                 android:id="@+id/title"
@@ -57,7 +57,7 @@
                 android:layout_marginEnd="24dp"
                 android:layout_marginStart="24dp"
                 android:layout_marginTop="24dp"
-                android:gravity="@integer/fingerprint_dialog_text_gravity"
+                android:gravity="@integer/biometric_dialog_text_gravity"
                 android:textSize="20sp"
                 android:maxLines="1"
                 android:singleLine="true"
@@ -72,7 +72,7 @@
                 android:layout_marginTop="8dp"
                 android:layout_marginStart="24dp"
                 android:layout_marginEnd="24dp"
-                android:gravity="@integer/fingerprint_dialog_text_gravity"
+                android:gravity="@integer/biometric_dialog_text_gravity"
                 android:textSize="16sp"
                 android:maxLines="1"
                 android:singleLine="true"
@@ -86,20 +86,19 @@
                 android:layout_height="wrap_content"
                 android:layout_marginEnd="24dp"
                 android:layout_marginStart="24dp"
-                android:gravity="@integer/fingerprint_dialog_text_gravity"
+                android:gravity="@integer/biometric_dialog_text_gravity"
                 android:paddingTop="8dp"
                 android:textSize="16sp"
                 android:maxLines="4"
                 android:textColor="@color/biometric_dialog_text_dark_color"/>
 
             <ImageView
-                android:id="@+id/fingerprint_icon"
+                android:id="@+id/biometric_icon"
                 android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
                 android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
                 android:layout_gravity="center_horizontal"
                 android:layout_marginTop="48dp"
-                android:scaleType="fitXY"
-                android:contentDescription="@string/accessibility_fingerprint_dialog_fingerprint_icon" />
+                android:scaleType="fitXY" />
 
             <TextView
                 android:id="@+id/error"
@@ -112,8 +111,7 @@
                 android:textSize="12sp"
                 android:gravity="center_horizontal"
                 android:accessibilityLiveRegion="polite"
-                android:text="@string/fingerprint_dialog_touch_sensor"
-                android:contentDescription="@string/accessibility_fingerprint_dialog_help_area"
+                android:contentDescription="@string/accessibility_biometric_dialog_help_area"
                 android:textColor="@color/biometric_dialog_text_light_color"/>
 
             <LinearLayout
@@ -125,7 +123,7 @@
                 android:orientation="horizontal"
                 android:measureWithLargestChild="true">
                 <Space android:id="@+id/leftSpacer"
-                    android:layout_width="24dp"
+                    android:layout_width="12dp"
                     android:layout_height="match_parent"
                     android:visibility="visible" />
                 <!-- Negative Button -->
@@ -133,20 +131,26 @@
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
                     style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-                    android:layout_marginStart="-12dp"
-                    android:gravity="start|center_vertical"
+                    android:gravity="center"
                     android:maxLines="2" />
+                <Space android:id="@+id/middleSpacer"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"
+                    android:visibility="visible" />
                 <!-- Positive Button -->
                 <Button android:id="@+id/button1"
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
-                    style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-                    android:layout_marginEnd="12dp"
-                    android:maxLines="2" />
+                    style="@*android:style/Widget.DeviceDefault.Button.Colored"
+                    android:gravity="center"
+                    android:maxLines="2"
+                    android:text="@string/biometric_dialog_confirm"
+                    android:visibility="gone"/>
                 <Space android:id="@+id/rightSpacer"
-                    android:layout_width="24dip"
+                    android:layout_width="12dip"
                     android:layout_height="match_parent"
-                    android:visibility="gone" />
+                    android:visibility="visible" />
             </LinearLayout>
         </LinearLayout>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d21f6d9..8ac3aa1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -985,7 +985,7 @@
     <!-- Biometric Dialog values -->
     <dimen name="biometric_dialog_biometric_icon_size">64dp</dimen>
     <dimen name="biometric_dialog_corner_size">4dp</dimen>
-    <dimen name="fingerprint_dialog_animation_translation_offset">350dp</dimen>
+    <dimen name="biometric_dialog_animation_translation_offset">350dp</dimen>
 
     <!-- Wireless Charging Animation values -->
     <dimen name="wireless_charging_dots_radius_start">0dp</dimen>
diff --git a/packages/SystemUI/res/values/integers.xml b/packages/SystemUI/res/values/integers.xml
index 87c4bbb..fd7a105 100644
--- a/packages/SystemUI/res/values/integers.xml
+++ b/packages/SystemUI/res/values/integers.xml
@@ -15,7 +15,7 @@
   ~ limitations under the License
   -->
 <resources>
-    <integer name="fingerprint_dialog_text_gravity">8388611</integer> <!-- gravity start -->
+    <integer name="biometric_dialog_text_gravity">8388611</integer> <!-- gravity start -->
 
     <!-- Action footer width used for layout_width to indicate WRAP_CONTENT (along with a weight of
          0) as we can allow the carrier text to stretch as far as needed in the QS footer. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 36f97cd..a3306d3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -257,14 +257,20 @@
     <!-- Button name for "Cancel". [CHAR LIMIT=NONE] -->
     <string name="cancel">Cancel</string>
 
+    <!-- Content description for the error/help message are when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_biometric_dialog_help_area">Help message area</string>
+    <!-- Message shown when a biometric is authenticated, asking the user to confirm authentication [CHAR LIMIT=30] -->
+    <string name="biometric_dialog_confirm">Confirm</string>
+
     <!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
     <string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
     <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_fingerprint_dialog_fingerprint_icon">Fingerprint icon</string>
-    <!-- Content description of the application icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
-    <string name="accessibility_fingerprint_dialog_app_icon">Application icon</string>
-    <!-- Content description for the error/help message are when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
-    <string name="accessibility_fingerprint_dialog_help_area">Help message area</string>
+
+    <!-- Message shown when the system-provided face dialog is shown, asking for authentication [CHAR LIMIT=30] -->
+    <string name="face_dialog_looking_for_face">Looking for you\u2026</string>
+    <!-- Content description of the face icon when the system-provided face dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_face_dialog_face_icon">Face icon</string>
 
     <!-- Content description of the compatibility zoom button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_compatibility_zoom_button">Compatibility zoom button.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index 327eba7..8fc4689 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.os.Bundle;
@@ -31,6 +32,9 @@
 import com.android.systemui.SystemUI;
 import com.android.systemui.statusbar.CommandQueue;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Receives messages sent from AuthenticationClient and shows the appropriate biometric UI (e.g.
  * BiometricDialogView).
@@ -48,7 +52,8 @@
     private static final int MSG_USER_CANCELED = 7;
     private static final int MSG_BUTTON_POSITIVE = 8;
 
-    private BiometricDialogView mDialogView;
+    private Map<Integer, BiometricDialogView> mDialogs; // BiometricAuthenticator type, view
+    private BiometricDialogView mCurrentDialog;
     private WindowManager mWindowManager;
     private IBiometricPromptReceiver mReceiver;
     private boolean mDialogShowing;
@@ -111,16 +116,25 @@
 
     @Override
     public void start() {
-        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
-            return;
+        final PackageManager pm = mContext.getPackageManager();
+        mDialogs = new HashMap<>();
+        if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+            mDialogs.put(BiometricAuthenticator.TYPE_FACE, new FaceDialogView(mContext, mCallback));
         }
-        getComponent(CommandQueue.class).addCallbacks(this);
-        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        mDialogView = new FingerprintDialogView(mContext, mCallback);
+        if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+            mDialogs.put(BiometricAuthenticator.TYPE_FINGERPRINT,
+                    new FingerprintDialogView(mContext, mCallback));
+        }
+
+        if (!mDialogs.isEmpty()) {
+            getComponent(CommandQueue.class).addCallbacks(this);
+            mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+        }
     }
 
     @Override
-    public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type) {
+    public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
+            boolean requireConfirmation) {
         if (DEBUG) Log.d(TAG, "showBiometricDialog, type: " + type);
         // Remove these messages as they are part of the previous client
         mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
@@ -129,6 +143,8 @@
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = bundle;
         args.arg2 = receiver;
+        args.argi1 = type;
+        args.arg3 = requireConfirmation;
         mHandler.obtainMessage(MSG_SHOW_DIALOG, args).sendToTarget();
     }
 
@@ -157,33 +173,41 @@
     }
 
     private void handleShowDialog(SomeArgs args) {
+        final int type = args.argi1;
+        mCurrentDialog = mDialogs.get(type);
+
         if (DEBUG) Log.d(TAG, "handleShowDialog, isAnimatingAway: "
-                + mDialogView.isAnimatingAway());
-        if (mDialogView.isAnimatingAway()) {
-            mDialogView.forceRemove();
+                + mCurrentDialog.isAnimatingAway() + " type: " + type);
+
+        if (mCurrentDialog.isAnimatingAway()) {
+            mCurrentDialog.forceRemove();
         } else if (mDialogShowing) {
             Log.w(TAG, "Dialog already showing");
             return;
         }
         mReceiver = (IBiometricPromptReceiver) args.arg2;
-        mDialogView.setBundle((Bundle)args.arg1);
-        mWindowManager.addView(mDialogView, mDialogView.getLayoutParams());
+        mCurrentDialog.setBundle((Bundle)args.arg1);
+        mCurrentDialog.setRequireConfirmation((boolean)args.arg3);
+        mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
         mDialogShowing = true;
     }
 
     private void handleBiometricAuthenticated() {
         if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated");
 
-        // TODO: announce correct string depending on modality
-        mDialogView.announceForAccessibility(
-                mContext.getResources().getText(
-                        com.android.internal.R.string.fingerprint_authenticated));
-        handleHideDialog(false /* userCanceled */);
+        mCurrentDialog.announceForAccessibility(
+                mContext.getResources()
+                        .getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
+        if (mCurrentDialog.requiresConfirmation()) {
+            mCurrentDialog.showConfirmationButton();
+        } else {
+            handleHideDialog(false /* userCanceled */);
+        }
     }
 
     private void handleBiometricHelp(String message) {
         if (DEBUG) Log.d(TAG, "handleBiometricHelp: " + message);
-        mDialogView.showHelpMessage(message);
+        mCurrentDialog.showHelpMessage(message);
     }
 
     private void handleBiometricError(String error) {
@@ -192,7 +216,7 @@
             if (DEBUG) Log.d(TAG, "Dialog already dismissed");
             return;
         }
-        mDialogView.showErrorMessage(error);
+        mCurrentDialog.showErrorMessage(error);
     }
 
     private void handleHideDialog(boolean userCanceled) {
@@ -212,7 +236,7 @@
         }
         mReceiver = null;
         mDialogShowing = false;
-        mDialogView.startDismiss();
+        mCurrentDialog.startDismiss();
     }
 
     private void handleButtonNegative() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index 50ee88c..c90861e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -36,6 +36,7 @@
 import android.view.WindowManager;
 import android.view.animation.Interpolator;
 import android.widget.Button;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -75,11 +76,12 @@
     private int mLastState;
     private boolean mAnimatingAway;
     private boolean mWasForceRemoved;
+    protected boolean mRequireConfirmation;
 
-    protected abstract int getLayoutResourceId();
-    protected abstract float getAnimationTranslationOffset();
     protected abstract void updateIcon(int lastState, int newState);
-    protected abstract int getHintStringResource();
+    protected abstract int getHintStringResourceId();
+    protected abstract int getAuthenticatedAccessibilityResourceId();
+    protected abstract int getIconDescriptionResourceId();
 
     private final Runnable mShowAnimationRunnable = new Runnable() {
         @Override
@@ -118,7 +120,8 @@
         mCallback = callback;
         mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        mAnimationTranslationOffset = getAnimationTranslationOffset();
+        mAnimationTranslationOffset = getResources()
+                .getDimension(R.dimen.biometric_dialog_animation_translation_offset);
         mErrorColor = Color.parseColor(
                 getResources().getString(R.color.biometric_dialog_error_color));
         mTextColor = Color.parseColor(
@@ -130,7 +133,7 @@
 
         // Create the dialog
         LayoutInflater factory = LayoutInflater.from(getContext());
-        mLayout = (ViewGroup) factory.inflate(getLayoutResourceId(), this, false);
+        mLayout = (ViewGroup) factory.inflate(R.layout.biometric_dialog, this, false);
         addView(mLayout);
 
         mDialog = mLayout.findViewById(R.id.dialog);
@@ -161,6 +164,10 @@
         final View rightSpace = mLayout.findViewById(R.id.right_space);
         final Button negative = mLayout.findViewById(R.id.button2);
         final Button positive = mLayout.findViewById(R.id.button1);
+        final ImageView icon = mLayout.findViewById(R.id.biometric_icon);
+
+        icon.setContentDescription(getResources().getString(getIconDescriptionResourceId()));
+        mErrorText.setText(getResources().getString(getHintStringResourceId()));
 
         setDismissesDialog(space);
         setDismissesDialog(leftSpace);
@@ -196,6 +203,8 @@
         title.setText(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE));
         title.setSelected(true);
 
+        positive.setVisibility(View.INVISIBLE);
+
         final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
         if (TextUtils.isEmpty(subtitleText)) {
             subtitle.setVisibility(View.GONE);
@@ -214,15 +223,6 @@
 
         negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
 
-        final CharSequence positiveText =
-                mBundle.getCharSequence(BiometricPrompt.KEY_POSITIVE_TEXT);
-        positive.setText(positiveText); // needs to be set for marquee to work
-        if (positiveText != null) {
-            positive.setVisibility(View.VISIBLE);
-        } else {
-            positive.setVisibility(View.GONE);
-        }
-
         if (!mWasForceRemoved) {
             // Dim the background and slide the dialog up
             mDialog.setTranslationY(mAnimationTranslationOffset);
@@ -299,6 +299,19 @@
         mBundle = bundle;
     }
 
+    public void setRequireConfirmation(boolean requireConfirmation) {
+        mRequireConfirmation = requireConfirmation;
+    }
+
+    public boolean requiresConfirmation() {
+        return mRequireConfirmation;
+    }
+
+    public void showConfirmationButton() {
+        final Button positive = mLayout.findViewById(R.id.button1);
+        positive.setVisibility(View.VISIBLE);
+    }
+
     public ViewGroup getLayout() {
         return mLayout;
     }
@@ -306,7 +319,7 @@
     // Clears the temporary message and shows the help message.
     private void handleClearMessage() {
         updateState(STATE_AUTHENTICATING);
-        mErrorText.setText(getHintStringResource());
+        mErrorText.setText(getHintStringResourceId());
         mErrorText.setTextColor(mTextColor);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
new file mode 100644
index 0000000..feef3a6d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+/**
+ * This class loads the view for the system-provided dialog. The view consists of:
+ * Application Icon, Title, Subtitle, Description, Fingerprint Icon, Error/Help message area,
+ * and positive/negative buttons.
+ */
+public class FaceDialogView extends BiometricDialogView {
+    public FaceDialogView(Context context,
+            DialogViewCallback callback) {
+        super(context, callback);
+    }
+
+    @Override
+    protected int getHintStringResourceId() {
+        return R.string.face_dialog_looking_for_face;
+    }
+
+    @Override
+    protected int getAuthenticatedAccessibilityResourceId() {
+        if (mRequireConfirmation) {
+            return com.android.internal.R.string.face_authenticated_confirmation_required;
+        } else {
+            return com.android.internal.R.string.face_authenticated_no_confirmation_required;
+        }
+    }
+
+    @Override
+    protected int getIconDescriptionResourceId() {
+        return R.string.accessibility_face_dialog_face_icon;
+    }
+
+    @Override
+    protected void updateIcon(int lastState, int newState) {
+        Drawable icon = mContext.getDrawable(R.drawable.face_dialog_icon);
+
+        final ImageView faceIcon = getLayout().findViewById(R.id.biometric_icon);
+        faceIcon.setImageDrawable(icon);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
index 9033322..38a69a9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
@@ -33,19 +33,18 @@
     private static final String TAG = "FingerprintDialogView";
 
     @Override
-    protected int getLayoutResourceId() {
-        return R.layout.fingerprint_dialog;
-    }
-
-    @Override
-    protected int getHintStringResource() {
+    protected int getHintStringResourceId() {
         return R.string.fingerprint_dialog_touch_sensor;
     }
 
     @Override
-    protected float getAnimationTranslationOffset() {
-        return getResources()
-                .getDimension(R.dimen.fingerprint_dialog_animation_translation_offset);
+    protected int getAuthenticatedAccessibilityResourceId() {
+        return com.android.internal.R.string.fingerprint_authenticated;
+    }
+
+    @Override
+    protected int getIconDescriptionResourceId() {
+        return R.string.accessibility_fingerprint_dialog_fingerprint_icon;
     }
 
     @Override
@@ -61,8 +60,8 @@
                 ? (AnimatedVectorDrawable) icon
                 : null;
 
-        final ImageView fingerprint_icon = getLayout().findViewById(R.id.fingerprint_icon);
-        fingerprint_icon.setImageDrawable(icon);
+        final ImageView fingerprintIcon = getLayout().findViewById(R.id.biometric_icon);
+        fingerprintIcon.setImageDrawable(icon);
 
         if (animation != null && shouldAnimateForTransition(lastState, newState)) {
             animation.forceAnimationOnUI();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index e19c844..5c0b328 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -161,7 +161,7 @@
         default void onRotationProposal(int rotation, boolean isValid) { }
 
         default void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver,
-                int type) { }
+                int type, boolean requireConfirmation) { }
         default void onBiometricAuthenticated() { }
         default void onBiometricHelp(String message) { }
         default void onBiometricError(String error) { }
@@ -514,12 +514,14 @@
     }
 
     @Override
-    public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type) {
+    public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
+            boolean requireConfirmation) {
         synchronized (mLock) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = bundle;
             args.arg2 = receiver;
             args.argi1 = type;
+            args.arg3 = requireConfirmation;
             mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
                     .sendToTarget();
         }
@@ -763,7 +765,8 @@
                         mCallbacks.get(i).showBiometricDialog(
                                 (Bundle) someArgs.arg1,
                                 (IBiometricPromptReceiver) someArgs.arg2,
-                                someArgs.argi1);
+                                someArgs.argi1,
+                                (boolean) someArgs.arg3);
                     }
                     someArgs.recycle();
                     break;