2/n: Add BiometricPrompt implicit UI
In small mode, tapping the gray are is ignored. Combined StatusBar#showBiometricTryAgain into onBiometricAuthenticated(bool) We now create a new BiometricDialogView object for each BiometricPrompt authenticate call. This makes the view's lifecycle much easier to manage. Bug: 111461540 Test: Small -> Big when error or rejected Test: Small -> Authenticated looks good Test: Try again button is shown when rejected Test: Icon spacing looks good after animation Test: Big/small state persists across configuration change Change-Id: Id0157a7506cea9b0e7de079c43f8bd5ba3cbd8c5
This commit is contained in:
@@ -153,13 +153,11 @@ oneway interface IStatusBar
|
||||
void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
|
||||
boolean requireConfirmation, int userId);
|
||||
// Used to hide the dialog when a biometric is authenticated
|
||||
void onBiometricAuthenticated();
|
||||
void onBiometricAuthenticated(boolean authenticated);
|
||||
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
|
||||
void onBiometricHelp(String message);
|
||||
// Used to set a message - the dialog will dismiss after a certain amount of time
|
||||
void onBiometricError(String error);
|
||||
// Used to hide the biometric dialog when the AuthenticationClient is stopped
|
||||
void hideBiometricDialog();
|
||||
// Used to request the "try again" button for authentications which requireConfirmation=true
|
||||
void showBiometricTryAgain();
|
||||
}
|
||||
|
||||
@@ -97,13 +97,11 @@ interface IStatusBarService
|
||||
void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
|
||||
boolean requireConfirmation, int userId);
|
||||
// Used to hide the dialog when a biometric is authenticated
|
||||
void onBiometricAuthenticated();
|
||||
void onBiometricAuthenticated(boolean authenticated);
|
||||
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
|
||||
void onBiometricHelp(String message);
|
||||
// Used to set a message - the dialog will dismiss after a certain amount of time
|
||||
void onBiometricError(String error);
|
||||
// Used to hide the biometric dialog when the AuthenticationClient is stopped
|
||||
void hideBiometricDialog();
|
||||
// Used to request the "try again" button for authentications which requireConfirmation=true
|
||||
void showBiometricTryAgain();
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="?android:attr/colorBackgroundFloating" />
|
||||
<corners android:radius="1dp"
|
||||
<corners
|
||||
android:topLeftRadius="@dimen/biometric_dialog_corner_size"
|
||||
android:topRightRadius="@dimen/biometric_dialog_corner_size"
|
||||
android:bottomLeftRadius="@dimen/biometric_dialog_corner_size"
|
||||
|
||||
@@ -33,9 +33,6 @@ import com.android.internal.os.SomeArgs;
|
||||
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).
|
||||
@@ -52,10 +49,8 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
|
||||
private static final int MSG_BUTTON_NEGATIVE = 6;
|
||||
private static final int MSG_USER_CANCELED = 7;
|
||||
private static final int MSG_BUTTON_POSITIVE = 8;
|
||||
private static final int MSG_BIOMETRIC_SHOW_TRY_AGAIN = 9;
|
||||
private static final int MSG_TRY_AGAIN_PRESSED = 10;
|
||||
private static final int MSG_TRY_AGAIN_PRESSED = 9;
|
||||
|
||||
private Map<Integer, BiometricDialogView> mDialogs; // BiometricAuthenticator type, view
|
||||
private SomeArgs mCurrentDialogArgs;
|
||||
private BiometricDialogView mCurrentDialog;
|
||||
private WindowManager mWindowManager;
|
||||
@@ -63,21 +58,22 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
|
||||
private boolean mDialogShowing;
|
||||
private Callback mCallback = new Callback();
|
||||
|
||||
private boolean mTryAgainShowing; // No good place to save state before config change :/
|
||||
private boolean mConfirmShowing; // No good place to save state before config change :/
|
||||
|
||||
private Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch(msg.what) {
|
||||
case MSG_SHOW_DIALOG:
|
||||
handleShowDialog((SomeArgs) msg.obj, false /* skipAnimation */);
|
||||
handleShowDialog((SomeArgs) msg.obj, false /* skipAnimation */,
|
||||
null /* savedState */);
|
||||
break;
|
||||
case MSG_BIOMETRIC_AUTHENTICATED:
|
||||
handleBiometricAuthenticated();
|
||||
handleBiometricAuthenticated((boolean) msg.obj);
|
||||
break;
|
||||
case MSG_BIOMETRIC_HELP:
|
||||
handleBiometricHelp((String) msg.obj);
|
||||
SomeArgs args = (SomeArgs) msg.obj;
|
||||
handleBiometricHelp((String) args.arg1 /* message */,
|
||||
(boolean) args.arg2 /* requireTryAgain */);
|
||||
args.recycle();
|
||||
break;
|
||||
case MSG_BIOMETRIC_ERROR:
|
||||
handleBiometricError((String) msg.obj);
|
||||
@@ -94,9 +90,6 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
|
||||
case MSG_BUTTON_POSITIVE:
|
||||
handleButtonPositive();
|
||||
break;
|
||||
case MSG_BIOMETRIC_SHOW_TRY_AGAIN:
|
||||
handleShowTryAgain();
|
||||
break;
|
||||
case MSG_TRY_AGAIN_PRESSED:
|
||||
handleTryAgainPressed();
|
||||
break;
|
||||
@@ -137,26 +130,15 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
createDialogs();
|
||||
|
||||
if (!mDialogs.isEmpty()) {
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
|
||||
|| pm.hasSystemFeature(PackageManager.FEATURE_FACE)
|
||||
|| pm.hasSystemFeature(PackageManager.FEATURE_IRIS)) {
|
||||
getComponent(CommandQueue.class).addCallback(this);
|
||||
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
|
||||
}
|
||||
}
|
||||
|
||||
private void createDialogs() {
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
mDialogs = new HashMap<>();
|
||||
if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
|
||||
mDialogs.put(BiometricAuthenticator.TYPE_FACE, new FaceDialogView(mContext, mCallback));
|
||||
}
|
||||
if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
|
||||
mDialogs.put(BiometricAuthenticator.TYPE_FINGERPRINT,
|
||||
new FingerprintDialogView(mContext, mCallback));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
|
||||
int type, boolean requireConfirmation, int userId) {
|
||||
@@ -179,15 +161,18 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBiometricAuthenticated() {
|
||||
if (DEBUG) Log.d(TAG, "onBiometricAuthenticated");
|
||||
mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
|
||||
public void onBiometricAuthenticated(boolean authenticated) {
|
||||
if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: " + authenticated);
|
||||
mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, authenticated).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBiometricHelp(String message) {
|
||||
if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
|
||||
mHandler.obtainMessage(MSG_BIOMETRIC_HELP, message).sendToTarget();
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = message;
|
||||
args.arg2 = false; // requireTryAgain
|
||||
mHandler.obtainMessage(MSG_BIOMETRIC_HELP, args).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -202,16 +187,21 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
|
||||
mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showBiometricTryAgain() {
|
||||
if (DEBUG) Log.d(TAG, "showBiometricTryAgain");
|
||||
mHandler.obtainMessage(MSG_BIOMETRIC_SHOW_TRY_AGAIN).sendToTarget();
|
||||
}
|
||||
|
||||
private void handleShowDialog(SomeArgs args, boolean skipAnimation) {
|
||||
private void handleShowDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
|
||||
mCurrentDialogArgs = args;
|
||||
final int type = args.argi1;
|
||||
mCurrentDialog = mDialogs.get(type);
|
||||
|
||||
if (type == BiometricAuthenticator.TYPE_FINGERPRINT) {
|
||||
mCurrentDialog = new FingerprintDialogView(mContext, mCallback);
|
||||
} else if (type == BiometricAuthenticator.TYPE_FACE) {
|
||||
mCurrentDialog = new FaceDialogView(mContext, mCallback);
|
||||
} else {
|
||||
Log.e(TAG, "Unsupported type: " + type);
|
||||
}
|
||||
|
||||
if (savedState != null) {
|
||||
mCurrentDialog.restoreState(savedState);
|
||||
}
|
||||
|
||||
if (DEBUG) Log.d(TAG, "handleShowDialog, isAnimatingAway: "
|
||||
+ mCurrentDialog.isAnimatingAway() + " type: " + type);
|
||||
@@ -227,32 +217,36 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
|
||||
mCurrentDialog.setRequireConfirmation((boolean) args.arg3);
|
||||
mCurrentDialog.setUserId(args.argi2);
|
||||
mCurrentDialog.setSkipIntro(skipAnimation);
|
||||
mCurrentDialog.setPendingTryAgain(mTryAgainShowing);
|
||||
mCurrentDialog.setPendingConfirm(mConfirmShowing);
|
||||
mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
|
||||
mDialogShowing = true;
|
||||
}
|
||||
|
||||
private void handleBiometricAuthenticated() {
|
||||
if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated");
|
||||
private void handleBiometricAuthenticated(boolean authenticated) {
|
||||
if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated: " + authenticated);
|
||||
|
||||
mCurrentDialog.announceForAccessibility(
|
||||
mContext.getResources()
|
||||
.getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
|
||||
if (mCurrentDialog.requiresConfirmation()) {
|
||||
mConfirmShowing = true;
|
||||
mCurrentDialog.showConfirmationButton(true /* show */);
|
||||
if (authenticated) {
|
||||
mCurrentDialog.announceForAccessibility(
|
||||
mContext.getResources()
|
||||
.getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
|
||||
if (mCurrentDialog.requiresConfirmation()) {
|
||||
mCurrentDialog.showConfirmationButton(true /* show */);
|
||||
} else {
|
||||
mCurrentDialog.updateState(BiometricDialogView.STATE_AUTHENTICATED);
|
||||
mHandler.postDelayed(() -> {
|
||||
handleHideDialog(false /* userCanceled */);
|
||||
}, mCurrentDialog.getDelayAfterAuthenticatedDurationMs());
|
||||
}
|
||||
} else {
|
||||
mCurrentDialog.updateState(BiometricDialogView.STATE_AUTHENTICATED);
|
||||
mHandler.postDelayed(() -> {
|
||||
handleHideDialog(false /* userCanceled */);
|
||||
}, mCurrentDialog.getDelayAfterAuthenticatedDurationMs());
|
||||
handleBiometricHelp(mContext.getResources()
|
||||
.getString(com.android.internal.R.string.biometric_not_recognized),
|
||||
true /* requireTryAgain */);
|
||||
mCurrentDialog.showTryAgainButton(true /* show */);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleBiometricHelp(String message) {
|
||||
private void handleBiometricHelp(String message, boolean requireTryAgain) {
|
||||
if (DEBUG) Log.d(TAG, "handleBiometricHelp: " + message);
|
||||
mCurrentDialog.showHelpMessage(message);
|
||||
mCurrentDialog.showHelpMessage(message, requireTryAgain);
|
||||
}
|
||||
|
||||
private void handleBiometricError(String error) {
|
||||
@@ -261,7 +255,6 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
|
||||
if (DEBUG) Log.d(TAG, "Dialog already dismissed");
|
||||
return;
|
||||
}
|
||||
mTryAgainShowing = false;
|
||||
mCurrentDialog.showErrorMessage(error);
|
||||
}
|
||||
|
||||
@@ -282,8 +275,6 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
|
||||
}
|
||||
mReceiver = null;
|
||||
mDialogShowing = false;
|
||||
mConfirmShowing = false;
|
||||
mTryAgainShowing = false;
|
||||
mCurrentDialog.startDismiss();
|
||||
}
|
||||
|
||||
@@ -297,7 +288,6 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Remote exception when handling negative button", e);
|
||||
}
|
||||
mTryAgainShowing = false;
|
||||
handleHideDialog(false /* userCanceled */);
|
||||
}
|
||||
|
||||
@@ -311,25 +301,16 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Remote exception when handling positive button", e);
|
||||
}
|
||||
mConfirmShowing = false;
|
||||
handleHideDialog(false /* userCanceled */);
|
||||
}
|
||||
|
||||
private void handleUserCanceled() {
|
||||
mTryAgainShowing = false;
|
||||
mConfirmShowing = false;
|
||||
handleHideDialog(true /* userCanceled */);
|
||||
}
|
||||
|
||||
private void handleShowTryAgain() {
|
||||
mCurrentDialog.showTryAgainButton(true /* show */);
|
||||
mTryAgainShowing = true;
|
||||
}
|
||||
|
||||
private void handleTryAgainPressed() {
|
||||
try {
|
||||
mCurrentDialog.clearTemporaryMessage();
|
||||
mTryAgainShowing = false;
|
||||
mReceiver.onTryAgainPressed();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "RemoteException when handling try again", e);
|
||||
@@ -340,13 +321,20 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
final boolean wasShowing = mDialogShowing;
|
||||
|
||||
// Save the state of the current dialog (buttons showing, etc)
|
||||
final Bundle savedState = new Bundle();
|
||||
if (mCurrentDialog != null) {
|
||||
mCurrentDialog.onSaveState(savedState);
|
||||
}
|
||||
|
||||
if (mDialogShowing) {
|
||||
mCurrentDialog.forceRemove();
|
||||
mDialogShowing = false;
|
||||
}
|
||||
createDialogs();
|
||||
|
||||
if (wasShowing) {
|
||||
handleShowDialog(mCurrentDialogArgs, true /* skipAnimation */);
|
||||
handleShowDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,12 +56,15 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
|
||||
private static final String TAG = "BiometricDialogView";
|
||||
|
||||
private static final String KEY_TRY_AGAIN_VISIBILITY = "key_try_again_visibility";
|
||||
private static final String KEY_CONFIRM_VISIBILITY = "key_confirm_visibility";
|
||||
|
||||
private static final int ANIMATION_DURATION_SHOW = 250; // ms
|
||||
private static final int ANIMATION_DURATION_AWAY = 350; // ms
|
||||
|
||||
private static final int MSG_CLEAR_MESSAGE = 1;
|
||||
|
||||
protected static final int STATE_NONE = 0;
|
||||
protected static final int STATE_IDLE = 0;
|
||||
protected static final int STATE_AUTHENTICATING = 1;
|
||||
protected static final int STATE_ERROR = 2;
|
||||
protected static final int STATE_PENDING_CONFIRMATION = 3;
|
||||
@@ -78,12 +81,19 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
private final float mDialogWidth;
|
||||
private final DialogViewCallback mCallback;
|
||||
|
||||
private ViewGroup mLayout;
|
||||
private final Button mPositiveButton;
|
||||
private final Button mNegativeButton;
|
||||
private final TextView mErrorText;
|
||||
protected final ViewGroup mLayout;
|
||||
protected final LinearLayout mDialog;
|
||||
protected final TextView mTitleText;
|
||||
protected final TextView mSubtitleText;
|
||||
protected final TextView mDescriptionText;
|
||||
protected final ImageView mBiometricIcon;
|
||||
protected final TextView mErrorText;
|
||||
protected final Button mPositiveButton;
|
||||
protected final Button mNegativeButton;
|
||||
protected final Button mTryAgainButton;
|
||||
|
||||
private Bundle mBundle;
|
||||
private final LinearLayout mDialog;
|
||||
|
||||
private int mLastState;
|
||||
private boolean mAnimatingAway;
|
||||
private boolean mWasForceRemoved;
|
||||
@@ -91,15 +101,13 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
protected boolean mRequireConfirmation;
|
||||
private int mUserId; // used to determine if we should show work background
|
||||
|
||||
private boolean mPendingShowTryAgain;
|
||||
private boolean mPendingShowConfirm;
|
||||
|
||||
protected abstract int getHintStringResourceId();
|
||||
protected abstract int getAuthenticatedAccessibilityResourceId();
|
||||
protected abstract int getIconDescriptionResourceId();
|
||||
protected abstract Drawable getAnimationForTransition(int oldState, int newState);
|
||||
protected abstract boolean shouldAnimateForTransition(int oldState, int newState);
|
||||
protected abstract int getDelayAfterAuthenticatedDurationMs();
|
||||
protected abstract boolean shouldGrayAreaDismissDialog();
|
||||
|
||||
private final Runnable mShowAnimationRunnable = new Runnable() {
|
||||
@Override
|
||||
@@ -124,7 +132,7 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
public void handleMessage(Message msg) {
|
||||
switch(msg.what) {
|
||||
case MSG_CLEAR_MESSAGE:
|
||||
handleClearMessage();
|
||||
handleClearMessage((boolean) msg.obj /* requireTryAgain */);
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unhandled message: " + msg.what);
|
||||
@@ -158,10 +166,6 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
mLayout = (ViewGroup) factory.inflate(R.layout.biometric_dialog, this, false);
|
||||
addView(mLayout);
|
||||
|
||||
mDialog = mLayout.findViewById(R.id.dialog);
|
||||
|
||||
mErrorText = mLayout.findViewById(R.id.error);
|
||||
|
||||
mLayout.setOnKeyListener(new View.OnKeyListener() {
|
||||
boolean downPressed = false;
|
||||
@Override
|
||||
@@ -184,12 +188,19 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
final View space = mLayout.findViewById(R.id.space);
|
||||
final View leftSpace = mLayout.findViewById(R.id.left_space);
|
||||
final View rightSpace = mLayout.findViewById(R.id.right_space);
|
||||
final ImageView icon = mLayout.findViewById(R.id.biometric_icon);
|
||||
final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
|
||||
|
||||
mDialog = mLayout.findViewById(R.id.dialog);
|
||||
mTitleText = mLayout.findViewById(R.id.title);
|
||||
mSubtitleText = mLayout.findViewById(R.id.subtitle);
|
||||
mDescriptionText = mLayout.findViewById(R.id.description);
|
||||
mBiometricIcon = mLayout.findViewById(R.id.biometric_icon);
|
||||
mErrorText = mLayout.findViewById(R.id.error);
|
||||
mNegativeButton = mLayout.findViewById(R.id.button2);
|
||||
mPositiveButton = mLayout.findViewById(R.id.button1);
|
||||
mTryAgainButton = mLayout.findViewById(R.id.button_try_again);
|
||||
|
||||
icon.setContentDescription(getResources().getString(getIconDescriptionResourceId()));
|
||||
mBiometricIcon.setContentDescription(
|
||||
getResources().getString(getIconDescriptionResourceId()));
|
||||
|
||||
setDismissesDialog(space);
|
||||
setDismissesDialog(leftSpace);
|
||||
@@ -206,8 +217,9 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
}, getDelayAfterAuthenticatedDurationMs());
|
||||
});
|
||||
|
||||
tryAgain.setOnClickListener((View v) -> {
|
||||
mTryAgainButton.setOnClickListener((View v) -> {
|
||||
showTryAgainButton(false /* show */);
|
||||
handleClearMessage(false /* requireTryAgain */);
|
||||
mCallback.onTryAgainPressed();
|
||||
});
|
||||
|
||||
@@ -215,15 +227,17 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
mLayout.requestFocus();
|
||||
}
|
||||
|
||||
public void onSaveState(Bundle bundle) {
|
||||
bundle.putInt(KEY_TRY_AGAIN_VISIBILITY, mTryAgainButton.getVisibility());
|
||||
bundle.putInt(KEY_CONFIRM_VISIBILITY, mPositiveButton.getVisibility());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
mErrorText.setText(getHintStringResourceId());
|
||||
|
||||
final TextView title = mLayout.findViewById(R.id.title);
|
||||
final TextView subtitle = mLayout.findViewById(R.id.subtitle);
|
||||
final TextView description = mLayout.findViewById(R.id.description);
|
||||
final ImageView backgroundView = mLayout.findViewById(R.id.background);
|
||||
|
||||
if (mUserManager.isManagedProfile(mUserId)) {
|
||||
@@ -244,36 +258,34 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
mDialog.getLayoutParams().width = (int) mDialogWidth;
|
||||
}
|
||||
|
||||
mLastState = STATE_NONE;
|
||||
mLastState = STATE_IDLE;
|
||||
updateState(STATE_AUTHENTICATING);
|
||||
|
||||
CharSequence titleText = mBundle.getCharSequence(BiometricPrompt.KEY_TITLE);
|
||||
|
||||
title.setText(titleText);
|
||||
title.setSelected(true);
|
||||
mTitleText.setVisibility(View.VISIBLE);
|
||||
mTitleText.setText(titleText);
|
||||
mTitleText.setSelected(true);
|
||||
|
||||
final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
|
||||
if (TextUtils.isEmpty(subtitleText)) {
|
||||
subtitle.setVisibility(View.GONE);
|
||||
mSubtitleText.setVisibility(View.GONE);
|
||||
} else {
|
||||
subtitle.setVisibility(View.VISIBLE);
|
||||
subtitle.setText(subtitleText);
|
||||
mSubtitleText.setVisibility(View.VISIBLE);
|
||||
mSubtitleText.setText(subtitleText);
|
||||
}
|
||||
|
||||
final CharSequence descriptionText =
|
||||
mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
|
||||
if (TextUtils.isEmpty(descriptionText)) {
|
||||
description.setVisibility(View.GONE);
|
||||
mDescriptionText.setVisibility(View.GONE);
|
||||
} else {
|
||||
description.setVisibility(View.VISIBLE);
|
||||
description.setText(descriptionText);
|
||||
mDescriptionText.setVisibility(View.VISIBLE);
|
||||
mDescriptionText.setText(descriptionText);
|
||||
}
|
||||
|
||||
mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
|
||||
|
||||
showTryAgainButton(mPendingShowTryAgain);
|
||||
showConfirmationButton(mPendingShowConfirm);
|
||||
|
||||
if (mWasForceRemoved || mSkipIntro) {
|
||||
// Show the dialog immediately
|
||||
mLayout.animate().cancel();
|
||||
@@ -302,8 +314,7 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
? (AnimatedVectorDrawable) icon
|
||||
: null;
|
||||
|
||||
final ImageView imageView = getLayout().findViewById(R.id.biometric_icon);
|
||||
imageView.setImageDrawable(icon);
|
||||
mBiometricIcon.setImageDrawable(icon);
|
||||
|
||||
if (animation != null && shouldAnimateForTransition(lastState, newState)) {
|
||||
animation.forceAnimationOnUI();
|
||||
@@ -314,7 +325,7 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
private void setDismissesDialog(View v) {
|
||||
v.setClickable(true);
|
||||
v.setOnTouchListener((View view, MotionEvent event) -> {
|
||||
if (mLastState != STATE_AUTHENTICATED) {
|
||||
if (mLastState != STATE_AUTHENTICATED && shouldGrayAreaDismissDialog()) {
|
||||
mCallback.onUserCanceled();
|
||||
}
|
||||
return true;
|
||||
@@ -331,11 +342,9 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
mWindowManager.removeView(BiometricDialogView.this);
|
||||
mAnimatingAway = false;
|
||||
// Set the icons / text back to normal state
|
||||
handleClearMessage();
|
||||
handleClearMessage(false /* requireTryAgain */);
|
||||
showTryAgainButton(false /* show */);
|
||||
mPendingShowTryAgain = false;
|
||||
mPendingShowConfirm = false;
|
||||
updateState(STATE_NONE);
|
||||
updateState(STATE_IDLE);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -412,35 +421,42 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
return mLayout;
|
||||
}
|
||||
|
||||
// Clears the temporary message and shows the help message.
|
||||
private void handleClearMessage() {
|
||||
updateState(STATE_AUTHENTICATING);
|
||||
mErrorText.setText(getHintStringResourceId());
|
||||
mErrorText.setTextColor(mTextColor);
|
||||
// Clears the temporary message and shows the help message. If requireTryAgain is true,
|
||||
// we will start the authenticating state again.
|
||||
private void handleClearMessage(boolean requireTryAgain) {
|
||||
if (!requireTryAgain) {
|
||||
updateState(STATE_AUTHENTICATING);
|
||||
mErrorText.setText(getHintStringResourceId());
|
||||
mErrorText.setTextColor(mTextColor);
|
||||
mErrorText.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
updateState(STATE_IDLE);
|
||||
mErrorText.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
// Shows an error/help message
|
||||
private void showTemporaryMessage(String message) {
|
||||
private void showTemporaryMessage(String message, boolean requireTryAgain) {
|
||||
mHandler.removeMessages(MSG_CLEAR_MESSAGE);
|
||||
updateState(STATE_ERROR);
|
||||
mErrorText.setText(message);
|
||||
mErrorText.setTextColor(mErrorColor);
|
||||
mErrorText.setContentDescription(message);
|
||||
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_MESSAGE),
|
||||
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_MESSAGE, requireTryAgain),
|
||||
BiometricPrompt.HIDE_DIALOG_DELAY);
|
||||
}
|
||||
|
||||
public void clearTemporaryMessage() {
|
||||
mHandler.removeMessages(MSG_CLEAR_MESSAGE);
|
||||
mHandler.obtainMessage(MSG_CLEAR_MESSAGE).sendToTarget();
|
||||
mHandler.obtainMessage(MSG_CLEAR_MESSAGE, false /* requireTryAgain */).sendToTarget();
|
||||
}
|
||||
|
||||
public void showHelpMessage(String message) {
|
||||
showTemporaryMessage(message);
|
||||
public void showHelpMessage(String message, boolean requireTryAgain) {
|
||||
showTemporaryMessage(message, requireTryAgain);
|
||||
}
|
||||
|
||||
public void showErrorMessage(String error) {
|
||||
showTemporaryMessage(error);
|
||||
showTemporaryMessage(error, false /* requireTryAgain */);
|
||||
showTryAgainButton(false /* show */);
|
||||
mCallback.onErrorShown();
|
||||
}
|
||||
@@ -459,22 +475,16 @@ public abstract class BiometricDialogView extends LinearLayout {
|
||||
}
|
||||
|
||||
public void showTryAgainButton(boolean show) {
|
||||
final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
|
||||
if (show) {
|
||||
tryAgain.setVisibility(View.VISIBLE);
|
||||
mTryAgainButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
tryAgain.setVisibility(View.GONE);
|
||||
mTryAgainButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the state before the window is attached, so we know if the dialog should be started
|
||||
// with or without the button. This is because there's no good onPause signal
|
||||
public void setPendingTryAgain(boolean show) {
|
||||
mPendingShowTryAgain = show;
|
||||
}
|
||||
|
||||
public void setPendingConfirm(boolean show) {
|
||||
mPendingShowConfirm = show;
|
||||
public void restoreState(Bundle bundle) {
|
||||
mTryAgainButton.setVisibility(bundle.getInt(KEY_TRY_AGAIN_VISIBILITY));
|
||||
mPositiveButton.setVisibility(bundle.getInt(KEY_CONFIRM_VISIBILITY));
|
||||
}
|
||||
|
||||
public WindowManager.LayoutParams getLayoutParams() {
|
||||
|
||||
@@ -16,8 +16,18 @@
|
||||
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.view.ViewOutlineProvider;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
@@ -28,13 +38,243 @@ import com.android.systemui.R;
|
||||
*/
|
||||
public class FaceDialogView extends BiometricDialogView {
|
||||
|
||||
private static final String TAG = "FaceDialogView";
|
||||
private static final String KEY_DIALOG_SIZE = "key_dialog_size";
|
||||
|
||||
private static final int HIDE_DIALOG_DELAY = 500; // ms
|
||||
private static final int IMPLICIT_Y_PADDING = 16; // dp
|
||||
private static final int GROW_DURATION = 150; // ms
|
||||
private static final int TEXT_ANIMATE_DISTANCE = 32; // dp
|
||||
|
||||
private static final int SIZE_UNKNOWN = 0;
|
||||
private static final int SIZE_SMALL = 1;
|
||||
private static final int SIZE_GROWING = 2;
|
||||
private static final int SIZE_BIG = 3;
|
||||
|
||||
private int mSize;
|
||||
private float mIconOriginalY;
|
||||
private DialogOutlineProvider mOutlineProvider = new DialogOutlineProvider();
|
||||
|
||||
private final class DialogOutlineProvider extends ViewOutlineProvider {
|
||||
|
||||
float mY;
|
||||
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
outline.setRoundRect(
|
||||
0 /* left */,
|
||||
(int) mY, /* top */
|
||||
mDialog.getWidth() /* right */,
|
||||
mDialog.getBottom(), /* bottom */
|
||||
getResources().getDimension(R.dimen.biometric_dialog_corner_size));
|
||||
}
|
||||
|
||||
int calculateSmall() {
|
||||
final float padding = dpToPixels(IMPLICIT_Y_PADDING);
|
||||
return mDialog.getHeight() - mBiometricIcon.getHeight() - 2 * (int) padding;
|
||||
}
|
||||
|
||||
void setOutlineY(float y) {
|
||||
mY = y;
|
||||
}
|
||||
}
|
||||
|
||||
public FaceDialogView(Context context,
|
||||
DialogViewCallback callback) {
|
||||
super(context, callback);
|
||||
}
|
||||
|
||||
private void updateSize(int newSize) {
|
||||
final float padding = dpToPixels(IMPLICIT_Y_PADDING);
|
||||
final float iconSmallPositionY = mDialog.getHeight() - mBiometricIcon.getHeight() - padding;
|
||||
|
||||
if (newSize == SIZE_SMALL) {
|
||||
// These fields are required and/or always hold a spot on the UI, so should be set to
|
||||
// INVISIBLE so they keep their position
|
||||
mTitleText.setVisibility(View.INVISIBLE);
|
||||
mErrorText.setVisibility(View.INVISIBLE);
|
||||
mNegativeButton.setVisibility(View.INVISIBLE);
|
||||
|
||||
// These fields are optional, so set them to gone or invisible depending on their
|
||||
// usage. If they're empty, they're already set to GONE in BiometricDialogView.
|
||||
if (!TextUtils.isEmpty(mSubtitleText.getText())) {
|
||||
mSubtitleText.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
if (!TextUtils.isEmpty(mDescriptionText.getText())) {
|
||||
mDescriptionText.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
// Move the biometric icon to the small spot
|
||||
mBiometricIcon.setY(iconSmallPositionY);
|
||||
|
||||
// Clip the dialog to the small size
|
||||
mDialog.setOutlineProvider(mOutlineProvider);
|
||||
mOutlineProvider.setOutlineY(mOutlineProvider.calculateSmall());
|
||||
|
||||
mDialog.setClipToOutline(true);
|
||||
mDialog.invalidateOutline();
|
||||
|
||||
mSize = newSize;
|
||||
} else if (mSize == SIZE_SMALL && newSize == SIZE_BIG) {
|
||||
mSize = SIZE_GROWING;
|
||||
|
||||
// Animate the outline
|
||||
final ValueAnimator outlineAnimator =
|
||||
ValueAnimator.ofFloat(mOutlineProvider.calculateSmall(), 0);
|
||||
outlineAnimator.addUpdateListener((animation) -> {
|
||||
final float y = (float) animation.getAnimatedValue();
|
||||
mOutlineProvider.setOutlineY(y);
|
||||
mDialog.invalidateOutline();
|
||||
});
|
||||
|
||||
// Animate the icon back to original big position
|
||||
final ValueAnimator iconAnimator =
|
||||
ValueAnimator.ofFloat(iconSmallPositionY, mIconOriginalY);
|
||||
iconAnimator.addUpdateListener((animation) -> {
|
||||
final float y = (float) animation.getAnimatedValue();
|
||||
mBiometricIcon.setY(y);
|
||||
});
|
||||
|
||||
// Animate the error text so it slides up with the icon
|
||||
final ValueAnimator textSlideAnimator =
|
||||
ValueAnimator.ofFloat(dpToPixels(TEXT_ANIMATE_DISTANCE), 0);
|
||||
textSlideAnimator.addUpdateListener((animation) -> {
|
||||
final float y = (float) animation.getAnimatedValue();
|
||||
mErrorText.setTranslationY(y);
|
||||
});
|
||||
|
||||
// Opacity animator for things that should fade in (title, subtitle, details, negative
|
||||
// button)
|
||||
final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(0, 1);
|
||||
opacityAnimator.addUpdateListener((animation) -> {
|
||||
final float opacity = (float) animation.getAnimatedValue();
|
||||
|
||||
// These fields are required and/or always hold a spot on the UI
|
||||
mTitleText.setAlpha(opacity);
|
||||
mErrorText.setAlpha(opacity);
|
||||
mNegativeButton.setAlpha(opacity);
|
||||
mTryAgainButton.setAlpha(opacity);
|
||||
|
||||
// These fields are optional, so only animate them if they're supposed to be showing
|
||||
if (!TextUtils.isEmpty(mSubtitleText.getText())) {
|
||||
mSubtitleText.setAlpha(opacity);
|
||||
}
|
||||
if (!TextUtils.isEmpty(mDescriptionText.getText())) {
|
||||
mDescriptionText.setAlpha(opacity);
|
||||
}
|
||||
});
|
||||
|
||||
// Choreograph together
|
||||
final AnimatorSet as = new AnimatorSet();
|
||||
as.setDuration(GROW_DURATION);
|
||||
as.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
super.onAnimationStart(animation);
|
||||
// Set the visibility of opacity-animating views back to VISIBLE
|
||||
mTitleText.setVisibility(View.VISIBLE);
|
||||
mErrorText.setVisibility(View.VISIBLE);
|
||||
mNegativeButton.setVisibility(View.VISIBLE);
|
||||
mTryAgainButton.setVisibility(View.VISIBLE);
|
||||
|
||||
if (!TextUtils.isEmpty(mSubtitleText.getText())) {
|
||||
mSubtitleText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
if (!TextUtils.isEmpty(mDescriptionText.getText())) {
|
||||
mDescriptionText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
mSize = SIZE_BIG;
|
||||
}
|
||||
});
|
||||
as.play(outlineAnimator).with(iconAnimator).with(opacityAnimator)
|
||||
.with(textSlideAnimator);
|
||||
as.start();
|
||||
} else if (mSize == SIZE_BIG) {
|
||||
mDialog.setClipToOutline(false);
|
||||
mDialog.invalidateOutline();
|
||||
|
||||
mBiometricIcon.setY(mIconOriginalY);
|
||||
|
||||
mSize = newSize;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveState(Bundle bundle) {
|
||||
super.onSaveState(bundle);
|
||||
bundle.putInt(KEY_DIALOG_SIZE, mSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreState(Bundle bundle) {
|
||||
super.restoreState(bundle);
|
||||
// Keep in mind that this happens before onAttachedToWindow()
|
||||
mSize = bundle.getInt(KEY_DIALOG_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do small/big layout here instead of onAttachedToWindow, since:
|
||||
* 1) We need the big layout to be measured, etc for small -> big animation
|
||||
* 2) We need the dialog measurements to know where to move the biometric icon to
|
||||
*
|
||||
* BiometricDialogView already sets the views to their default big state, so here we only
|
||||
* need to hide the ones that are unnecessary.
|
||||
*/
|
||||
@Override
|
||||
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
|
||||
if (mIconOriginalY == 0) {
|
||||
mIconOriginalY = mBiometricIcon.getY();
|
||||
}
|
||||
|
||||
// UNKNOWN means size hasn't been set yet. First time we create the dialog.
|
||||
// onLayout can happen when visibility of views change (during animation, etc).
|
||||
if (mSize != SIZE_UNKNOWN) {
|
||||
// Probably not the cleanest way to do this, but since dialog is big by default,
|
||||
// and small dialogs can persist across orientation changes, we need to set it to
|
||||
// small size here again.
|
||||
if (mSize == SIZE_SMALL) {
|
||||
updateSize(SIZE_SMALL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If we don't require confirmation, show the small dialog first (until errors occur).
|
||||
if (!requiresConfirmation()) {
|
||||
updateSize(SIZE_SMALL);
|
||||
} else {
|
||||
updateSize(SIZE_BIG);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showErrorMessage(String error) {
|
||||
super.showErrorMessage(error);
|
||||
|
||||
// All error messages will cause the dialog to go from small -> big. Error messages
|
||||
// are messages such as lockout, auth failed, etc.
|
||||
if (mSize == SIZE_SMALL) {
|
||||
updateSize(SIZE_BIG);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showTryAgainButton(boolean show) {
|
||||
if (show && mSize == SIZE_SMALL) {
|
||||
// Do not call super, we will nicely animate the alpha together with the rest
|
||||
// of the elements in here.
|
||||
updateSize(SIZE_BIG);
|
||||
} else {
|
||||
super.showTryAgainButton(show);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getHintStringResourceId() {
|
||||
return R.string.face_dialog_looking_for_face;
|
||||
@@ -56,7 +296,9 @@ public class FaceDialogView extends BiometricDialogView {
|
||||
|
||||
@Override
|
||||
protected boolean shouldAnimateForTransition(int oldState, int newState) {
|
||||
if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
|
||||
if (oldState == STATE_ERROR && newState == STATE_IDLE) {
|
||||
return true;
|
||||
} else if (oldState == STATE_IDLE && newState == STATE_AUTHENTICATING) {
|
||||
return false;
|
||||
} else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
|
||||
return true;
|
||||
@@ -77,10 +319,20 @@ public class FaceDialogView extends BiometricDialogView {
|
||||
return HIDE_DIALOG_DELAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldGrayAreaDismissDialog() {
|
||||
if (mSize == SIZE_SMALL) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Drawable getAnimationForTransition(int oldState, int newState) {
|
||||
int iconRes;
|
||||
if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
|
||||
if (oldState == STATE_ERROR && newState == STATE_IDLE) {
|
||||
iconRes = R.drawable.face_dialog_error_to_face;
|
||||
} else if (oldState == STATE_IDLE && newState == STATE_AUTHENTICATING) {
|
||||
iconRes = R.drawable.face_dialog_face_to_error;
|
||||
} else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
|
||||
iconRes = R.drawable.face_dialog_face_to_error;
|
||||
@@ -97,4 +349,14 @@ public class FaceDialogView extends BiometricDialogView {
|
||||
}
|
||||
return mContext.getDrawable(iconRes);
|
||||
}
|
||||
|
||||
private float dpToPixels(float dp) {
|
||||
return dp * ((float) mContext.getResources().getDisplayMetrics().densityDpi
|
||||
/ DisplayMetrics.DENSITY_DEFAULT);
|
||||
}
|
||||
|
||||
private float pixelsToDp(float pixels) {
|
||||
return pixels / ((float) mContext.getResources().getDisplayMetrics().densityDpi
|
||||
/ DisplayMetrics.DENSITY_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class FingerprintDialogView extends BiometricDialogView {
|
||||
|
||||
@Override
|
||||
protected boolean shouldAnimateForTransition(int oldState, int newState) {
|
||||
if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
|
||||
if (oldState == STATE_IDLE && newState == STATE_AUTHENTICATING) {
|
||||
return false;
|
||||
} else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
|
||||
return true;
|
||||
@@ -67,10 +67,16 @@ public class FingerprintDialogView extends BiometricDialogView {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldGrayAreaDismissDialog() {
|
||||
// Fingerprint dialog always dismisses when region outside the dialog is tapped
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Drawable getAnimationForTransition(int oldState, int newState) {
|
||||
int iconRes;
|
||||
if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
|
||||
if (oldState == STATE_IDLE && newState == STATE_AUTHENTICATING) {
|
||||
iconRes = R.drawable.fingerprint_dialog_fp_to_error;
|
||||
} else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
|
||||
iconRes = R.drawable.fingerprint_dialog_fp_to_error;
|
||||
|
||||
@@ -111,7 +111,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
|
||||
private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT;
|
||||
private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
|
||||
private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT;
|
||||
private static final int MSG_BIOMETRIC_TRY_AGAIN = 47 << MSG_SHIFT;
|
||||
|
||||
public static final int FLAG_EXCLUDE_NONE = 0;
|
||||
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
|
||||
@@ -271,11 +270,10 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
|
||||
|
||||
default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
|
||||
int type, boolean requireConfirmation, int userId) { }
|
||||
default void onBiometricAuthenticated() { }
|
||||
default void onBiometricAuthenticated(boolean authenticated) { }
|
||||
default void onBiometricHelp(String message) { }
|
||||
default void onBiometricError(String error) { }
|
||||
default void hideBiometricDialog() { }
|
||||
default void showBiometricTryAgain() { }
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -736,9 +734,9 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBiometricAuthenticated() {
|
||||
public void onBiometricAuthenticated(boolean authenticated) {
|
||||
synchronized (mLock) {
|
||||
mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
|
||||
mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, authenticated).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -763,13 +761,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showBiometricTryAgain() {
|
||||
synchronized (mLock) {
|
||||
mHandler.obtainMessage(MSG_BIOMETRIC_TRY_AGAIN).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private final class H extends Handler {
|
||||
private H(Looper l) {
|
||||
super(l);
|
||||
@@ -991,7 +982,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
|
||||
break;
|
||||
case MSG_BIOMETRIC_AUTHENTICATED:
|
||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||
mCallbacks.get(i).onBiometricAuthenticated();
|
||||
mCallbacks.get(i).onBiometricAuthenticated((boolean) msg.obj);
|
||||
}
|
||||
break;
|
||||
case MSG_BIOMETRIC_HELP:
|
||||
@@ -1024,11 +1015,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
|
||||
mCallbacks.get(i).showPinningEscapeToast();
|
||||
}
|
||||
break;
|
||||
case MSG_BIOMETRIC_TRY_AGAIN:
|
||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||
mCallbacks.get(i).showBiometricTryAgain();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,7 +395,7 @@ public class BiometricService extends SystemService {
|
||||
|
||||
// Notify SysUI that the biometric has been authenticated. SysUI already knows
|
||||
// the implicit/explicit state and will react accordingly.
|
||||
mStatusBarService.onBiometricAuthenticated();
|
||||
mStatusBarService.onBiometricAuthenticated(true);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Remote exception", e);
|
||||
}
|
||||
@@ -412,17 +412,20 @@ public class BiometricService extends SystemService {
|
||||
return;
|
||||
}
|
||||
|
||||
mStatusBarService.onBiometricHelp(getContext().getResources().getString(
|
||||
com.android.internal.R.string.biometric_not_recognized));
|
||||
if (requireConfirmation) {
|
||||
mStatusBarService.onBiometricAuthenticated(false);
|
||||
|
||||
// TODO: This logic will need to be updated if BP is multi-modal
|
||||
if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
|
||||
// Pause authentication. onBiometricAuthenticated(false) causes the
|
||||
// dialog to show a "try again" button for passive modalities.
|
||||
mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
|
||||
mStatusBarService.showBiometricTryAgain();
|
||||
// Cancel authentication. Skip the token/package check since we are
|
||||
// cancelling from system server. The interface is permission protected so
|
||||
// this is fine.
|
||||
cancelInternal(null /* token */, null /* package */,
|
||||
false /* fromClient */);
|
||||
}
|
||||
|
||||
mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Remote exception", e);
|
||||
@@ -579,8 +582,10 @@ public class BiometricService extends SystemService {
|
||||
}
|
||||
|
||||
if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
|
||||
final boolean mContinuing = mCurrentAuthSession != null
|
||||
&& mCurrentAuthSession.mState == STATE_AUTH_PAUSED;
|
||||
final boolean continuing = mCurrentAuthSession != null &&
|
||||
(mCurrentAuthSession.mState == STATE_AUTH_PAUSED
|
||||
|| mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED);
|
||||
|
||||
mCurrentAuthSession = mPendingAuthSession;
|
||||
mPendingAuthSession = null;
|
||||
|
||||
@@ -602,7 +607,7 @@ public class BiometricService extends SystemService {
|
||||
modality |= pair.getKey();
|
||||
}
|
||||
|
||||
if (!mContinuing) {
|
||||
if (!continuing) {
|
||||
mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
|
||||
mInternalReceiver, modality, requireConfirmation, userId);
|
||||
mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
|
||||
|
||||
@@ -598,11 +598,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBiometricAuthenticated() {
|
||||
public void onBiometricAuthenticated(boolean authenticated) {
|
||||
enforceBiometricDialog();
|
||||
if (mBar != null) {
|
||||
try {
|
||||
mBar.onBiometricAuthenticated();
|
||||
mBar.onBiometricAuthenticated(authenticated);
|
||||
} catch (RemoteException ex) {
|
||||
}
|
||||
}
|
||||
@@ -641,17 +641,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showBiometricTryAgain() {
|
||||
enforceBiometricDialog();
|
||||
if (mBar != null) {
|
||||
try {
|
||||
mBar.showBiometricTryAgain();
|
||||
} catch (RemoteException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/117478341): make it aware of multi-display if needed.
|
||||
@Override
|
||||
public void disable(int what, IBinder token, String pkg) {
|
||||
|
||||
Reference in New Issue
Block a user