diff options
| author | 2019-09-16 21:35:32 +0000 | |
|---|---|---|
| committer | 2019-09-16 21:35:32 +0000 | |
| commit | c336db8d9b91ce22d5a9324adbd558cc05003c67 (patch) | |
| tree | 6df09b27a05a5442668e2a6a3750ad21e31a59db | |
| parent | c48da419931a3a9272910f2631f815cecf05c9ba (diff) | |
| parent | d837cedb92bb484ed224c4a6bb1fe2b588a161fd (diff) | |
Merge "10/n: Remove old biometric UI"
5 files changed, 8 insertions, 1573 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 7c6792ce5d48..d10a3fede412 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -29,7 +29,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; -import android.provider.Settings; import android.util.Log; import android.view.WindowManager; @@ -46,8 +45,6 @@ import java.util.List; */ public class AuthController extends SystemUI implements CommandQueue.Callbacks, AuthDialogCallback { - private static final String DISABLE_NEW_DIALOG = - "com.android.systemui.biometrics.AuthController.DISABLE_NEW_DIALOG"; private static final String TAG = "BiometricPrompt/AuthController"; private static final boolean DEBUG = true; @@ -316,25 +313,13 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, protected AuthDialog buildDialog(Bundle biometricPromptBundle, boolean requireConfirmation, int userId, int type, String opPackageName, boolean skipIntro) { - if (Settings.Secure.getIntForUser( - mContext.getContentResolver(), DISABLE_NEW_DIALOG, userId, 0) == 0) { - return new AuthContainerView.Builder(mContext) - .setCallback(this) - .setBiometricPromptBundle(biometricPromptBundle) - .setRequireConfirmation(requireConfirmation) - .setUserId(userId) - .setOpPackageName(opPackageName) - .setSkipIntro(skipIntro) - .build(type); - } else { - return new BiometricDialogView.Builder(mContext) - .setCallback(this) - .setBiometricPromptBundle(biometricPromptBundle) - .setRequireConfirmation(requireConfirmation) - .setUserId(userId) - .setOpPackageName(opPackageName) - .setSkipIntro(skipIntro) - .build(type); - } + return new AuthContainerView.Builder(mContext) + .setCallback(this) + .setBiometricPromptBundle(biometricPromptBundle) + .setRequireConfirmation(requireConfirmation) + .setUserId(userId) + .setOpPackageName(opPackageName) + .setSkipIntro(skipIntro) + .build(type); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java deleted file mode 100644 index b985e1c2a4d4..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java +++ /dev/null @@ -1,963 +0,0 @@ -/* - * 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 static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ValueAnimator; -import android.annotation.IntDef; -import android.annotation.Nullable; -import android.app.admin.DevicePolicyManager; -import android.content.Context; -import android.graphics.Outline; -import android.graphics.PixelFormat; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.hardware.biometrics.BiometricAuthenticator; -import android.hardware.biometrics.BiometricPrompt; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.UserManager; -import android.text.TextUtils; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewOutlineProvider; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.animation.Interpolator; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dependency; -import com.android.systemui.Interpolators; -import com.android.systemui.R; -import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.util.leak.RotationUtils; - -/** - * Abstract base class. Shows a dialog for BiometricPrompt. - */ -public abstract class BiometricDialogView extends LinearLayout implements AuthDialog { - - private static final String TAG = "BiometricPrompt/DialogView"; - - public static final String KEY_TRY_AGAIN_VISIBILITY = "key_try_again_visibility"; - public static final String KEY_CONFIRM_VISIBILITY = "key_confirm_visibility"; - public static final String KEY_CONFIRM_ENABLED = "key_confirm_enabled"; - public static final String KEY_STATE = "key_state"; - public static final String KEY_ERROR_TEXT_VISIBILITY = "key_error_text_visibility"; - public static final String KEY_ERROR_TEXT_STRING = "key_error_text_string"; - public static final String KEY_ERROR_TEXT_IS_TEMPORARY = "key_error_text_is_temporary"; - public static final String KEY_ERROR_TEXT_COLOR = "key_error_text_color"; - public static final String KEY_DIALOG_SIZE = "key_dialog_size"; - - private static final int ANIMATION_DURATION_SHOW = 250; // ms - private static final int ANIMATION_DURATION_AWAY = 350; // ms - - protected static final int MSG_RESET_MESSAGE = 1; - - 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; - protected static final int STATE_AUTHENTICATED = 4; - - // Dialog layout/animation - 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 - @VisibleForTesting static final int SIZE_UNKNOWN = 0; - @VisibleForTesting static final int SIZE_SMALL = 1; - @VisibleForTesting static final int SIZE_GROWING = 2; - @VisibleForTesting static final int SIZE_BIG = 3; - @IntDef({SIZE_UNKNOWN, SIZE_SMALL, SIZE_GROWING, SIZE_BIG}) - @interface DialogSize {} - - @VisibleForTesting final WakefulnessLifecycle mWakefulnessLifecycle; - private final AccessibilityManager mAccessibilityManager; - private final IBinder mWindowToken = new Binder(); - private final Interpolator mLinearOutSlowIn; - private final WindowManager mWindowManager; - private final UserManager mUserManager; - private final DevicePolicyManager mDevicePolicyManager; - private final float mAnimationTranslationOffset; - private final int mErrorColor; - private final float mDialogWidth; - protected final AuthDialogCallback mCallback; - private final DialogOutlineProvider mOutlineProvider = new DialogOutlineProvider(); - - protected final ViewGroup mLayout; - protected final LinearLayout mDialog; - @VisibleForTesting final TextView mTitleText; - @VisibleForTesting final TextView mSubtitleText; - @VisibleForTesting final TextView mDescriptionText; - @VisibleForTesting final ImageView mBiometricIcon; - @VisibleForTesting final TextView mErrorText; - @VisibleForTesting final Button mPositiveButton; - @VisibleForTesting final Button mNegativeButton; - @VisibleForTesting final Button mTryAgainButton; - - protected final int mTextColor; - - private Bundle mBundle; - private Bundle mRestoredState; - private String mOpPackageName; - - private int mState = STATE_IDLE; - private boolean mWasForceRemoved; - private boolean mSkipIntro; - protected boolean mRequireConfirmation; - private int mUserId; // used to determine if we should show work background - private @DialogSize int mSize; - private float mIconOriginalY; - - private boolean mCompletedAnimatingIn; - private boolean mPendingDismissDialog; - - protected abstract int getHintStringResourceId(); - protected abstract int getAuthenticatedAccessibilityResourceId(); - protected abstract int getIconDescriptionResourceId(); - protected abstract int getDelayAfterAuthenticatedDurationMs(); - protected abstract boolean shouldGrayAreaDismissDialog(); - protected abstract void handleResetMessage(); - protected abstract void updateIcon(int oldState, int newState); - protected abstract boolean supportsSmallDialog(); - - private final Runnable mShowAnimationRunnable = new Runnable() { - @Override - public void run() { - mLayout.animate() - .alpha(1f) - .setDuration(ANIMATION_DURATION_SHOW) - .setInterpolator(mLinearOutSlowIn) - .withLayer() - .start(); - mDialog.animate() - .translationY(0) - .setDuration(ANIMATION_DURATION_SHOW) - .setInterpolator(mLinearOutSlowIn) - .withLayer() - .withEndAction(() -> onDialogAnimatedIn()) - .start(); - } - }; - - @VisibleForTesting - final WakefulnessLifecycle.Observer mWakefulnessObserver = - new WakefulnessLifecycle.Observer() { - @Override - public void onStartedGoingToSleep() { - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); - } - }; - - 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 = Utils.dpToPixels(mContext, IMPLICIT_Y_PADDING); - return mDialog.getHeight() - mBiometricIcon.getHeight() - 2 * (int) padding; - } - - void setOutlineY(float y) { - mY = y; - } - } - - protected Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch(msg.what) { - case MSG_RESET_MESSAGE: - handleResetMessage(); - break; - default: - Log.e(TAG, "Unhandled message: " + msg.what); - break; - } - } - }; - - /** - * Builds the dialog with specified parameters. - */ - public static class Builder { - public static final int TYPE_FINGERPRINT = BiometricAuthenticator.TYPE_FINGERPRINT; - public static final int TYPE_FACE = BiometricAuthenticator.TYPE_FACE; - - private Context mContext; - private AuthDialogCallback mCallback; - private Bundle mBundle; - private boolean mRequireConfirmation; - private int mUserId; - private String mOpPackageName; - private boolean mSkipIntro; - - public Builder(Context context) { - mContext = context; - } - - public Builder setCallback(AuthDialogCallback callback) { - mCallback = callback; - return this; - } - - public Builder setBiometricPromptBundle(Bundle bundle) { - mBundle = bundle; - return this; - } - - public Builder setRequireConfirmation(boolean requireConfirmation) { - mRequireConfirmation = requireConfirmation; - return this; - } - - public Builder setUserId(int userId) { - mUserId = userId; - return this; - } - - public Builder setOpPackageName(String opPackageName) { - mOpPackageName = opPackageName; - return this; - } - - public Builder setSkipIntro(boolean skipIntro) { - mSkipIntro = skipIntro; - return this; - } - - public BiometricDialogView build(int type) { - return build(type, new Injector()); - } - - public BiometricDialogView build(int type, Injector injector) { - BiometricDialogView dialog; - if (type == TYPE_FINGERPRINT) { - dialog = new FingerprintDialogView(mContext, mCallback, injector); - } else if (type == TYPE_FACE) { - dialog = new FaceDialogView(mContext, mCallback, injector); - } else { - return null; - } - dialog.setBundle(mBundle); - dialog.setRequireConfirmation(mRequireConfirmation); - dialog.setUserId(mUserId); - dialog.setOpPackageName(mOpPackageName); - dialog.setSkipIntro(mSkipIntro); - return dialog; - } - } - - public static class Injector { - public WakefulnessLifecycle getWakefulnessLifecycle() { - return Dependency.get(WakefulnessLifecycle.class); - } - } - - protected BiometricDialogView(Context context, AuthDialogCallback callback, Injector injector) { - super(context); - mWakefulnessLifecycle = injector.getWakefulnessLifecycle(); - - mCallback = callback; - mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN; - mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); - mWindowManager = mContext.getSystemService(WindowManager.class); - mUserManager = mContext.getSystemService(UserManager.class); - mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class); - mAnimationTranslationOffset = getResources() - .getDimension(R.dimen.biometric_dialog_animation_translation_offset); - mErrorColor = getResources().getColor(R.color.biometric_dialog_error); - mTextColor = getResources().getColor(R.color.biometric_dialog_gray); - - DisplayMetrics metrics = new DisplayMetrics(); - mWindowManager.getDefaultDisplay().getMetrics(metrics); - mDialogWidth = Math.min(metrics.widthPixels, metrics.heightPixels); - - // Create the dialog - LayoutInflater factory = LayoutInflater.from(getContext()); - mLayout = (ViewGroup) factory.inflate(R.layout.biometric_dialog, this, false); - addView(mLayout); - - mLayout.setOnKeyListener(new View.OnKeyListener() { - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - if (keyCode != KeyEvent.KEYCODE_BACK) { - return false; - } - if (event.getAction() == KeyEvent.ACTION_UP) { - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); - } - return true; - } - }); - - 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); - - 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); - - mBiometricIcon.setContentDescription( - getResources().getString(getIconDescriptionResourceId())); - - setDismissesDialog(space); - setDismissesDialog(leftSpace); - setDismissesDialog(rightSpace); - - mNegativeButton.setOnClickListener((View v) -> { - if (mState == STATE_PENDING_CONFIRMATION || mState == STATE_AUTHENTICATED) { - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); - } else { - animateAway(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE); - } - }); - - mPositiveButton.setOnClickListener((View v) -> { - updateState(STATE_AUTHENTICATED); - mHandler.postDelayed(() -> { - animateAway(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE); - }, getDelayAfterAuthenticatedDurationMs()); - }); - - mTryAgainButton.setOnClickListener((View v) -> { - handleResetMessage(); - updateState(STATE_AUTHENTICATING); - showTryAgainButton(false /* show */); - - mPositiveButton.setVisibility(View.VISIBLE); - mPositiveButton.setEnabled(false); - - mCallback.onTryAgainPressed(); - }); - - // Must set these in order for the back button events to be received. - mLayout.setFocusableInTouchMode(true); - mLayout.requestFocus(); - } - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - - mWakefulnessLifecycle.addObserver(mWakefulnessObserver); - - final ImageView backgroundView = mLayout.findViewById(R.id.background); - if (mUserManager.isManagedProfile(mUserId)) { - final Drawable image = getResources().getDrawable(R.drawable.work_challenge_background, - mContext.getTheme()); - image.setColorFilter(mDevicePolicyManager.getOrganizationColorForUser(mUserId), - PorterDuff.Mode.DARKEN); - backgroundView.setScaleType(ImageView.ScaleType.CENTER_CROP); - backgroundView.setImageDrawable(image); - } else { - backgroundView.setImageDrawable(null); - backgroundView.setBackgroundColor(R.color.biometric_dialog_dim_color); - } - - mNegativeButton.setVisibility(View.VISIBLE); - mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT)); - - if (RotationUtils.getRotation(mContext) != RotationUtils.ROTATION_NONE) { - mDialog.getLayoutParams().width = (int) mDialogWidth; - } - - if (mRestoredState == null) { - updateState(STATE_AUTHENTICATING); - final int hint = getHintStringResourceId(); - if (hint != 0) { - mErrorText.setText(hint); - mErrorText.setContentDescription(mContext.getString(hint)); - mErrorText.setVisibility(View.VISIBLE); - } else { - mErrorText.setVisibility(View.INVISIBLE); - } - announceAccessibilityEvent(); - } else { - updateState(mState); - } - - CharSequence titleText = mBundle.getCharSequence(BiometricPrompt.KEY_TITLE); - - mTitleText.setVisibility(View.VISIBLE); - mTitleText.setText(titleText); - - final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE); - if (TextUtils.isEmpty(subtitleText)) { - mSubtitleText.setVisibility(View.GONE); - announceAccessibilityEvent(); - } else { - mSubtitleText.setVisibility(View.VISIBLE); - mSubtitleText.setText(subtitleText); - } - - final CharSequence descriptionText = - mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION); - if (TextUtils.isEmpty(descriptionText)) { - mDescriptionText.setVisibility(View.GONE); - announceAccessibilityEvent(); - } else { - mDescriptionText.setVisibility(View.VISIBLE); - mDescriptionText.setText(descriptionText); - } - - if (requiresConfirmation() && mRestoredState == null) { - mPositiveButton.setVisibility(View.VISIBLE); - mPositiveButton.setEnabled(false); - } - - if (mWasForceRemoved || mSkipIntro) { - // Show the dialog immediately - mLayout.animate().cancel(); - mDialog.animate().cancel(); - mDialog.setAlpha(1.0f); - mDialog.setTranslationY(0); - mLayout.setAlpha(1.0f); - mCompletedAnimatingIn = true; - } else { - // Dim the background and slide the dialog up - mDialog.setTranslationY(mAnimationTranslationOffset); - mLayout.setAlpha(0f); - postOnAnimation(mShowAnimationRunnable); - } - mWasForceRemoved = false; - mSkipIntro = false; - } - - /** - * 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 (getSize() != 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 (getSize() == SIZE_SMALL) { - updateSize(SIZE_SMALL); - } - return; - } - - // If we don't require confirmation, show the small dialog first (until errors occur). - if (!requiresConfirmation() && supportsSmallDialog()) { - updateSize(SIZE_SMALL); - } else { - updateSize(SIZE_BIG); - } - } - - @Override - public void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - mWakefulnessLifecycle.removeObserver(mWakefulnessObserver); - } - - @VisibleForTesting - void updateSize(@DialogSize int newSize) { - final float padding = Utils.dpToPixels(mContext, IMPLICIT_Y_PADDING); - final float iconSmallPositionY = mDialog.getHeight() - mBiometricIcon.getHeight() - padding; - - if (newSize == SIZE_SMALL) { - if (!supportsSmallDialog()) { - Log.e(TAG, "Small dialog unsupported"); - return; - } - - // 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; - announceAccessibilityEvent(); - } 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(Utils.dpToPixels(mContext, 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; - } - } - - private void setDismissesDialog(View v) { - v.setClickable(true); - v.setOnClickListener(v1 -> { - if (mState != STATE_AUTHENTICATED && shouldGrayAreaDismissDialog()) { - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); - } - }); - } - - private void animateAway(@AuthDialogCallback.DismissedReason int reason) { - animateAway(true /* sendReason */, reason); - } - - /** - * Animate the dialog away - * @param reason one of the {@link AuthDialogCallback} codes - */ - private void animateAway(boolean sendReason, @AuthDialogCallback.DismissedReason int reason) { - if (!mCompletedAnimatingIn) { - Log.w(TAG, "startDismiss(): waiting for onDialogAnimatedIn"); - mPendingDismissDialog = true; - return; - } - - // This is where final cleanup should occur. - final Runnable endActionRunnable = new Runnable() { - @Override - public void run() { - mWindowManager.removeView(BiometricDialogView.this); - // Set the icons / text back to normal state - handleResetMessage(); - showTryAgainButton(false /* show */); - updateState(STATE_IDLE); - if (sendReason) { - mCallback.onDismissed(reason); - } - } - }; - - postOnAnimation(new Runnable() { - @Override - public void run() { - mLayout.animate() - .alpha(0f) - .setDuration(ANIMATION_DURATION_AWAY) - .setInterpolator(mLinearOutSlowIn) - .withLayer() - .start(); - mDialog.animate() - .translationY(mAnimationTranslationOffset) - .setDuration(ANIMATION_DURATION_AWAY) - .setInterpolator(mLinearOutSlowIn) - .withLayer() - .withEndAction(endActionRunnable) - .start(); - } - }); - } - - /** - * Skip the intro animation - */ - private void setSkipIntro(boolean skip) { - mSkipIntro = skip; - } - - private void setBundle(Bundle bundle) { - mBundle = bundle; - } - - private void setRequireConfirmation(boolean requireConfirmation) { - mRequireConfirmation = requireConfirmation; - } - - protected boolean requiresConfirmation() { - return mRequireConfirmation; - } - - private void setUserId(int userId) { - mUserId = userId; - } - - private void setOpPackageName(String opPackageName) { - mOpPackageName = opPackageName; - } - - // Shows an error/help message - protected void showTemporaryMessage(String message) { - mHandler.removeMessages(MSG_RESET_MESSAGE); - mErrorText.setText(message); - mErrorText.setTextColor(mErrorColor); - mErrorText.setContentDescription(message); - mErrorText.setVisibility(View.VISIBLE); - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE), - BiometricPrompt.HIDE_DIALOG_DELAY); - } - - @Override - public void show(WindowManager wm, @Nullable Bundle savedState) { - if (savedState != null) { - restoreState(savedState); - } - wm.addView(this, getLayoutParams(mWindowToken)); - } - - /** - * Force remove the window, cancelling any animation that's happening. This should only be - * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method - * will cause the dialog to show without an animation the next time it's attached. - */ - @Override - public void dismissWithoutCallback(boolean animate) { - if (animate) { - animateAway(false /* sendReason */, 0 /* reason */); - } else { - mLayout.animate().cancel(); - mDialog.animate().cancel(); - mWindowManager.removeView(BiometricDialogView.this); - mWasForceRemoved = true; - } - } - - @Override - public void dismissFromSystemServer() { - animateAway(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER); - } - - @Override - public void onAuthenticationSucceeded() { - announceForAccessibility(getResources().getText(getAuthenticatedAccessibilityResourceId())); - - if (requiresConfirmation()) { - updateState(STATE_PENDING_CONFIRMATION); - } else { - mHandler.postDelayed(() -> { - animateAway(AuthDialogCallback.DISMISSED_AUTHENTICATED); - }, getDelayAfterAuthenticatedDurationMs()); - - updateState(STATE_AUTHENTICATED); - } - } - - - @Override - public void onAuthenticationFailed(String message) { - updateState(STATE_ERROR); - showTemporaryMessage(message); - } - - /** - * Transient help message (acquire) is received, dialog stays showing. Sensor stays in - * "authenticating" state. - * @param message - */ - @Override - public void onHelp(String message) { - updateState(STATE_ERROR); - showTemporaryMessage(message); - } - - /** - * Hard error is received, dialog will be dismissed soon. - * @param error - */ - @Override - public void onError(String 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); - } - - updateState(STATE_ERROR); - showTemporaryMessage(error); - showTryAgainButton(false /* show */); - - mHandler.postDelayed(() -> { - animateAway(AuthDialogCallback.DISMISSED_ERROR); - }, BiometricPrompt.HIDE_DIALOG_DELAY); - } - - - @Override - public void onSaveState(Bundle bundle) { - bundle.putInt(KEY_TRY_AGAIN_VISIBILITY, mTryAgainButton.getVisibility()); - bundle.putInt(KEY_CONFIRM_VISIBILITY, mPositiveButton.getVisibility()); - bundle.putBoolean(KEY_CONFIRM_ENABLED, mPositiveButton.isEnabled()); - bundle.putInt(KEY_STATE, mState); - bundle.putInt(KEY_ERROR_TEXT_VISIBILITY, mErrorText.getVisibility()); - bundle.putCharSequence(KEY_ERROR_TEXT_STRING, mErrorText.getText()); - bundle.putBoolean(KEY_ERROR_TEXT_IS_TEMPORARY, mHandler.hasMessages(MSG_RESET_MESSAGE)); - bundle.putInt(KEY_ERROR_TEXT_COLOR, mErrorText.getCurrentTextColor()); - bundle.putInt(KEY_DIALOG_SIZE, mSize); - } - - public void restoreState(Bundle bundle) { - mRestoredState = bundle; - - // Keep in mind that this happens before onAttachedToWindow() - mSize = bundle.getInt(KEY_DIALOG_SIZE); - - final int tryAgainVisibility = bundle.getInt(KEY_TRY_AGAIN_VISIBILITY); - mTryAgainButton.setVisibility(tryAgainVisibility); - final int confirmVisibility = bundle.getInt(KEY_CONFIRM_VISIBILITY); - mPositiveButton.setVisibility(confirmVisibility); - final boolean confirmEnabled = bundle.getBoolean(KEY_CONFIRM_ENABLED); - mPositiveButton.setEnabled(confirmEnabled); - mState = bundle.getInt(KEY_STATE); - mErrorText.setText(bundle.getCharSequence(KEY_ERROR_TEXT_STRING)); - mErrorText.setContentDescription(bundle.getCharSequence(KEY_ERROR_TEXT_STRING)); - final int errorTextVisibility = bundle.getInt(KEY_ERROR_TEXT_VISIBILITY); - mErrorText.setVisibility(errorTextVisibility); - if (errorTextVisibility == View.INVISIBLE || tryAgainVisibility == View.INVISIBLE - || confirmVisibility == View.INVISIBLE) { - announceAccessibilityEvent(); - } - mErrorText.setTextColor(bundle.getInt(KEY_ERROR_TEXT_COLOR)); - if (bundle.getBoolean(KEY_ERROR_TEXT_IS_TEMPORARY)) { - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE), - BiometricPrompt.HIDE_DIALOG_DELAY); - } - } - - @Override - public String getOpPackageName() { - return mOpPackageName; - } - - protected void updateState(int newState) { - if (newState == STATE_PENDING_CONFIRMATION) { - mHandler.removeMessages(MSG_RESET_MESSAGE); - mErrorText.setTextColor(mTextColor); - mErrorText.setText(R.string.biometric_dialog_tap_confirm); - mErrorText.setContentDescription( - getResources().getString(R.string.biometric_dialog_tap_confirm)); - mErrorText.setVisibility(View.VISIBLE); - announceAccessibilityEvent(); - mPositiveButton.setVisibility(View.VISIBLE); - mPositiveButton.setEnabled(true); - } else if (newState == STATE_AUTHENTICATED) { - mPositiveButton.setVisibility(View.GONE); - mNegativeButton.setVisibility(View.GONE); - mErrorText.setVisibility(View.INVISIBLE); - announceAccessibilityEvent(); - } - - if (newState == STATE_PENDING_CONFIRMATION || newState == STATE_AUTHENTICATED) { - mNegativeButton.setText(R.string.cancel); - mNegativeButton.setContentDescription(getResources().getString(R.string.cancel)); - } - - updateIcon(mState, newState); - mState = newState; - } - - protected @DialogSize int getSize() { - return mSize; - } - - protected void showTryAgainButton(boolean show) { - if (show && getSize() == 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 { - if (show) { - mTryAgainButton.setVisibility(View.VISIBLE); - } else { - mTryAgainButton.setVisibility(View.GONE); - announceAccessibilityEvent(); - } - } - - if (show) { - mPositiveButton.setVisibility(View.GONE); - announceAccessibilityEvent(); - } - } - - protected void onDialogAnimatedIn() { - mCompletedAnimatingIn = true; - - if (mPendingDismissDialog) { - Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now"); - animateAway(false /* sendReason */, 0); - mPendingDismissDialog = false; - } - } - - /** - * @param windowToken token for the window - * @return - */ - public static WindowManager.LayoutParams getLayoutParams(IBinder windowToken) { - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, - WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, - PixelFormat.TRANSLUCENT); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; - lp.setTitle("BiometricDialogView"); - lp.token = windowToken; - return lp; - } - - // Every time a view becomes invisible we need to announce an accessibility event. - // This is due to an issue in the framework, b/132298701 recommended this workaround. - protected void announceAccessibilityEvent() { - if (!mAccessibilityManager.isEnabled()) { - return; - } - AccessibilityEvent event = AccessibilityEvent.obtain(); - event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); - event.setContentChangeTypes(CONTENT_CHANGE_TYPE_SUBTREE); - mDialog.sendAccessibilityEventUnchecked(event); - mDialog.notifySubtreeAccessibilityStateChanged(mDialog, mDialog, - CONTENT_CHANGE_TYPE_SUBTREE); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java deleted file mode 100644 index d5dcbf126b63..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * 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.Animatable2; -import android.graphics.drawable.AnimatedVectorDrawable; -import android.graphics.drawable.Drawable; -import android.hardware.biometrics.BiometricPrompt; -import android.os.Bundle; -import android.util.Log; -import android.view.View; - -import com.android.systemui.R; - -/** - * This class loads the view for the system-provided dialog. The view consists of: - * Application Icon, Title, Subtitle, Description, Biometric Icon, Error/Help message area, - * and positive/negative buttons. - */ -public class FaceDialogView extends BiometricDialogView { - - private static final String TAG = "BiometricPrompt/FaceDialogView"; - - private static final String KEY_DIALOG_ANIMATED_IN = "key_dialog_animated_in"; - - private static final int HIDE_DIALOG_DELAY = 500; // ms - - private IconController mIconController; - private boolean mDialogAnimatedIn; - - /** - * Class that handles the biometric icon animations. - */ - private final class IconController extends Animatable2.AnimationCallback { - - private boolean mLastPulseDirection; // false = dark to light, true = light to dark - - int mState; - - IconController() { - mState = STATE_IDLE; - } - - public void animateOnce(int iconRes) { - animateIcon(iconRes, false); - } - - public void showStatic(int iconRes) { - mBiometricIcon.setImageDrawable(mContext.getDrawable(iconRes)); - } - - public void startPulsing() { - mLastPulseDirection = false; - animateIcon(R.drawable.face_dialog_pulse_dark_to_light, true); - } - - public void showIcon(int iconRes) { - final Drawable drawable = mContext.getDrawable(iconRes); - mBiometricIcon.setImageDrawable(drawable); - } - - private void animateIcon(int iconRes, boolean repeat) { - final AnimatedVectorDrawable icon = - (AnimatedVectorDrawable) mContext.getDrawable(iconRes); - mBiometricIcon.setImageDrawable(icon); - icon.forceAnimationOnUI(); - if (repeat) { - icon.registerAnimationCallback(this); - } - icon.start(); - } - - private void pulseInNextDirection() { - int iconRes = mLastPulseDirection ? R.drawable.face_dialog_pulse_dark_to_light - : R.drawable.face_dialog_pulse_light_to_dark; - animateIcon(iconRes, true /* repeat */); - mLastPulseDirection = !mLastPulseDirection; - } - - @Override - public void onAnimationEnd(Drawable drawable) { - super.onAnimationEnd(drawable); - - if (mState == STATE_AUTHENTICATING) { - // Still authenticating, pulse the icon - pulseInNextDirection(); - } - } - } - - private final Runnable mErrorToIdleAnimationRunnable = () -> { - updateState(STATE_IDLE); - mErrorText.setVisibility(View.INVISIBLE); - announceAccessibilityEvent(); - }; - - protected FaceDialogView(Context context, AuthDialogCallback callback, Injector injector) { - super(context, callback, injector); - mIconController = new IconController(); - } - - @Override - public void onSaveState(Bundle bundle) { - super.onSaveState(bundle); - bundle.putBoolean(KEY_DIALOG_ANIMATED_IN, mDialogAnimatedIn); - } - - - @Override - protected void handleResetMessage() { - mErrorText.setTextColor(mTextColor); - mErrorText.setVisibility(View.INVISIBLE); - announceAccessibilityEvent(); - } - - @Override - public void restoreState(Bundle bundle) { - super.restoreState(bundle); - mDialogAnimatedIn = bundle.getBoolean(KEY_DIALOG_ANIMATED_IN); - } - - @Override - public void onAuthenticationFailed(String message) { - super.onAuthenticationFailed(message); - showTryAgainButton(true); - } - - @Override - protected int getHintStringResourceId() { - return 0; - } - - @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 oldState, int newState) { - mIconController.mState = newState; - - if (newState == STATE_AUTHENTICATING) { - mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); - if (mDialogAnimatedIn) { - mIconController.startPulsing(); - } else { - mIconController.showIcon(R.drawable.face_dialog_pulse_dark_to_light); - } - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_authenticating)); - } else if (oldState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) { - mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark); - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_confirmed)); - } else if (oldState == STATE_ERROR && newState == STATE_IDLE) { - mIconController.animateOnce(R.drawable.face_dialog_error_to_idle); - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_idle)); - } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATED) { - mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); - mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark); - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_authenticated)); - } else if (newState == STATE_ERROR) { - // It's easier to only check newState and gate showing the animation on the - // mErrorToIdleAnimationRunnable as a proxy, than add a ton of extra state. For example, - // we may go from error -> error due to configuration change which is valid and we - // should show the animation, or we can go from error -> error by receiving repeated - // acquire messages in which case we do not want to repeatedly start the animation. - if (!mHandler.hasCallbacks(mErrorToIdleAnimationRunnable)) { - mIconController.animateOnce(R.drawable.face_dialog_dark_to_error); - mHandler.postDelayed(mErrorToIdleAnimationRunnable, - BiometricPrompt.HIDE_DIALOG_DELAY); - } - } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) { - mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark); - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_authenticated)); - } else if (newState == STATE_PENDING_CONFIRMATION) { - mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); - mIconController.animateOnce(R.drawable.face_dialog_wink_from_dark); - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_authenticated)); - } else if (newState == STATE_IDLE) { - mIconController.showStatic(R.drawable.face_dialog_idle_static); - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_idle)); - } else { - Log.w(TAG, "Unknown animation from " + oldState + " -> " + newState); - } - - // Note that this must be after the newState == STATE_ERROR check above since this affects - // the logic. - if (oldState == STATE_ERROR && newState == STATE_ERROR) { - // Keep the error icon and text around for a while longer if we keep receiving - // STATE_ERROR - mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); - mHandler.postDelayed(mErrorToIdleAnimationRunnable, BiometricPrompt.HIDE_DIALOG_DELAY); - } - } - - @Override - protected boolean supportsSmallDialog() { - return true; - } - - @Override - public void onDialogAnimatedIn() { - super.onDialogAnimatedIn(); - mDialogAnimatedIn = true; - mIconController.startPulsing(); - } - - @Override - protected int getDelayAfterAuthenticatedDurationMs() { - return HIDE_DIALOG_DELAY; - } - - @Override - protected boolean shouldGrayAreaDismissDialog() { - if (getSize() == SIZE_SMALL) { - return false; - } - return true; - } - - -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java deleted file mode 100644 index cda217619eed..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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.AnimatedVectorDrawable; -import android.graphics.drawable.Drawable; -import android.util.Log; - -import com.android.systemui.R; - -/** - * This class loads the view for the system-provided dialog. The view consists of: - * Application Icon, Title, Subtitle, Description, Biometric Icon, Error/Help message area, - * and positive/negative buttons. - */ -public class FingerprintDialogView extends BiometricDialogView { - - private static final String TAG = "BiometricPrompt/FingerprintDialogView"; - - protected FingerprintDialogView(Context context, AuthDialogCallback callback, - Injector injector) { - super(context, callback, injector); - } - - @Override - protected void handleResetMessage() { - updateState(STATE_AUTHENTICATING); - mErrorText.setText(getHintStringResourceId()); - mErrorText.setTextColor(mTextColor); - } - - @Override - protected int getHintStringResourceId() { - return R.string.fingerprint_dialog_touch_sensor; - } - - @Override - protected int getAuthenticatedAccessibilityResourceId() { - return com.android.internal.R.string.fingerprint_authenticated; - } - - @Override - protected int getIconDescriptionResourceId() { - return R.string.accessibility_fingerprint_dialog_fingerprint_icon; - } - - @Override - protected void updateIcon(int lastState, int newState) { - final Drawable icon = getAnimationForTransition(lastState, newState); - if (icon == null) { - Log.e(TAG, "Animation not found, " + lastState + " -> " + newState); - return; - } - - final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable - ? (AnimatedVectorDrawable) icon - : null; - - mBiometricIcon.setImageDrawable(icon); - - if (animation != null && shouldAnimateForTransition(lastState, newState)) { - animation.forceAnimationOnUI(); - animation.start(); - } - } - - @Override - protected boolean supportsSmallDialog() { - return false; - } - - protected boolean shouldAnimateForTransition(int oldState, int newState) { - if (newState == STATE_ERROR) { - return true; - } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) { - return true; - } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) { - // TODO(b/77328470): add animation when fingerprint is authenticated - return false; - } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATED) { - // TODO(b/77328470): add animation when fingerprint is authenticated - return false; - } else if (newState == STATE_AUTHENTICATING) { - return false; - } - return false; - } - - @Override - protected int getDelayAfterAuthenticatedDurationMs() { - return 0; - } - - @Override - protected boolean shouldGrayAreaDismissDialog() { - // Fingerprint dialog always dismisses when region outside the dialog is tapped - return true; - } - - protected Drawable getAnimationForTransition(int oldState, int newState) { - int iconRes; - if (newState == STATE_ERROR) { - iconRes = R.drawable.fingerprint_dialog_fp_to_error; - } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) { - iconRes = R.drawable.fingerprint_dialog_error_to_fp; - } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) { - // TODO(b/77328470): add animation when fingerprint is authenticated - iconRes = R.drawable.fingerprint_dialog_fp_to_error; - } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATED) { - // TODO(b/77328470): add animation when fingerprint is authenticated - iconRes = R.drawable.fingerprint_dialog_fp_to_error; - } else if (newState == STATE_AUTHENTICATING) { - iconRes = R.drawable.fingerprint_dialog_fp_to_error; - } else { - return null; - } - return mContext.getDrawable(iconRes); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogViewTest.java deleted file mode 100644 index 3ff1f383fdee..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogViewTest.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2019 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 static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotSame; -import static junit.framework.Assert.assertTrue; - -import static org.mockito.Mockito.spy; - -import android.app.admin.DevicePolicyManager; -import android.content.Context; -import android.hardware.biometrics.BiometricPrompt; -import android.os.Bundle; -import android.os.UserManager; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableContext; -import android.testing.TestableLooper.RunWithLooper; -import android.view.View; - -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.keyguard.WakefulnessLifecycle; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -@SmallTest -public class BiometricDialogViewTest extends SysuiTestCase { - - FaceDialogView mFaceDialogView; - - private static final String TITLE = "Title"; - private static final String SUBTITLE = "Subtitle"; - private static final String DESCRIPTION = "Description"; - private static final String NEGATIVE_BUTTON = "Negative Button"; - - private static final String TEST_HELP = "Help"; - - TestableContext mTestableContext; - @Mock - private AuthDialogCallback mCallback; - @Mock - private UserManager mUserManager; - @Mock - private DevicePolicyManager mDpm; - - private static class Injector extends BiometricDialogView.Injector { - @Override - public WakefulnessLifecycle getWakefulnessLifecycle() { - final WakefulnessLifecycle lifecycle = new WakefulnessLifecycle(); - lifecycle.dispatchFinishedWakingUp(); - return lifecycle; - } - } - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - mTestableContext = spy(mContext); - mTestableContext.addMockSystemService(UserManager.class, mUserManager); - mTestableContext.addMockSystemService(DevicePolicyManager.class, mDpm); - } - - @Test - public void testContentStates_confirmationRequired_authenticated() { - mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback, - true /* requireConfirmation */); - mFaceDialogView.onAttachedToWindow(); - - // When starting authentication - assertEquals(View.VISIBLE, mFaceDialogView.mTitleText.getVisibility()); - assertEquals(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility()); - assertEquals(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility()); - assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility()); - assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility()); - assertEquals(View.VISIBLE, mFaceDialogView.mNegativeButton.getVisibility()); - assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility()); - - // Contents are as expected - assertTrue(TITLE.contentEquals(mFaceDialogView.mTitleText.getText())); - assertTrue(SUBTITLE.contentEquals(mFaceDialogView.mSubtitleText.getText())); - assertTrue(DESCRIPTION.contentEquals(mFaceDialogView.mDescriptionText.getText())); - assertTrue(mFaceDialogView.mPositiveButton.getText().toString() - .contentEquals(mContext.getString(R.string.biometric_dialog_confirm))); - assertTrue(NEGATIVE_BUTTON.contentEquals(mFaceDialogView.mNegativeButton.getText())); - assertTrue(mFaceDialogView.mTryAgainButton.getText().toString() - .contentEquals(mContext.getString(R.string.biometric_dialog_try_again))); - - // When help message is received - mFaceDialogView.onHelp(TEST_HELP); - assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE); - assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText())); - - // When authenticated, confirm button comes out - mFaceDialogView.onAuthenticationSucceeded(); - assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility()); - assertEquals(true, mFaceDialogView.mPositiveButton.isEnabled()); - } - - @Test - public void testContentStates_confirmationNotRequired_authenticated() { - mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback, - false /* requireConfirmation */); - mFaceDialogView.onAttachedToWindow(); - mFaceDialogView.updateSize(FaceDialogView.SIZE_SMALL); - - assertEquals(View.INVISIBLE, mFaceDialogView.mTitleText.getVisibility()); - assertNotSame(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility()); - assertNotSame(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility()); - assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility()); - assertEquals(View.GONE, mFaceDialogView.mPositiveButton.getVisibility()); - assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility()); - assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility()); - } - - @Test - public void testContentStates_confirmationNotRequired_help() { - mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback, - false /* requireConfirmation */); - mFaceDialogView.onAttachedToWindow(); - - mFaceDialogView.onHelp(TEST_HELP); - assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE); - assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText())); - } - - @Test - public void testBack_sendsUserCanceled() { - // TODO: Need robolectric framework to wait for handler to complete - } - - @Test - public void testScreenOff_sendsUserCanceled() { - // TODO: Need robolectric framework to wait for handler to complete - } - - @Test - public void testRestoreState_contentStatesCorrect() { - mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback, - false /* requireConfirmation */); - mFaceDialogView.onAttachedToWindow(); - mFaceDialogView.onAuthenticationFailed(TEST_HELP); - - final Bundle bundle = new Bundle(); - mFaceDialogView.onSaveState(bundle); - - mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback, - false /* requireConfirmation */); - mFaceDialogView.restoreState(bundle); - mFaceDialogView.onAttachedToWindow(); - - assertEquals(View.VISIBLE, mFaceDialogView.mTryAgainButton.getVisibility()); - } - - private FaceDialogView buildFaceDialogView(Context context, AuthDialogCallback callback, - boolean requireConfirmation) { - return (FaceDialogView) new BiometricDialogView.Builder(context) - .setCallback(callback) - .setBiometricPromptBundle(createTestDialogBundle()) - .setRequireConfirmation(requireConfirmation) - .setUserId(0) - .setOpPackageName("test_package") - .build(BiometricDialogView.Builder.TYPE_FACE, new Injector()); - } - - private Bundle createTestDialogBundle() { - Bundle bundle = new Bundle(); - - bundle.putCharSequence(BiometricPrompt.KEY_TITLE, TITLE); - bundle.putCharSequence(BiometricPrompt.KEY_SUBTITLE, SUBTITLE); - bundle.putCharSequence(BiometricPrompt.KEY_DESCRIPTION, DESCRIPTION); - bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, NEGATIVE_BUTTON); - - // RequireConfirmation is a hint to BiometricService. This can be forced to be required - // by user settings, and should be tested in BiometricService. - bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true); - - return bundle; - } -} |