Loading packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java +251 −19 Original line number Diff line number Diff line Loading @@ -18,8 +18,14 @@ package com.android.systemui.biometrics.ui; 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.app.admin.DevicePolicyManager; import android.content.Context; import android.graphics.Outline; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; Loading @@ -38,6 +44,7 @@ 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; Loading Loading @@ -71,6 +78,7 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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 Loading @@ -83,8 +91,18 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet protected static final int STATE_PENDING_CONFIRMATION = 3; protected static final int STATE_AUTHENTICATED = 4; @VisibleForTesting final WakefulnessLifecycle mWakefulnessLifecycle; // 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; Loading @@ -95,25 +113,18 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet private final int mErrorColor; private final float mDialogWidth; protected final DialogViewCallback 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; @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; Loading @@ -126,6 +137,8 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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; Loading @@ -137,6 +150,7 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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 Loading Loading @@ -166,6 +180,30 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet } }; 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) { Loading Loading @@ -346,7 +384,6 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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()); Loading Loading @@ -428,6 +465,42 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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(); Loading @@ -435,6 +508,133 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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 -> { Loading Loading @@ -599,6 +799,12 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet */ @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 */); Loading @@ -619,11 +825,16 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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); } @Override 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); Loading Loading @@ -678,7 +889,28 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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() { Loading packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java +8 −248 File changed.Preview size limit exceeded, changes collapsed. Show changes packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java +5 −0 Original line number Diff line number Diff line Loading @@ -80,6 +80,11 @@ public class FingerprintDialogView extends BiometricDialogView { } } @Override protected boolean supportsSmallDialog() { return false; } protected boolean shouldAnimateForTransition(int oldState, int newState) { if (newState == STATE_ERROR) { return true; Loading packages/SystemUI/src/com/android/systemui/biometrics/ui/Utils.java 0 → 100644 +32 −0 Original line number Diff line number Diff line /* * 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.ui; import android.content.Context; import android.util.DisplayMetrics; public class Utils { static float dpToPixels(Context context, float dp) { return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT); } static float pixelsToDp(Context context, float pixels) { return pixels / ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT); } } Loading
packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java +251 −19 Original line number Diff line number Diff line Loading @@ -18,8 +18,14 @@ package com.android.systemui.biometrics.ui; 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.app.admin.DevicePolicyManager; import android.content.Context; import android.graphics.Outline; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; Loading @@ -38,6 +44,7 @@ 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; Loading Loading @@ -71,6 +78,7 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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 Loading @@ -83,8 +91,18 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet protected static final int STATE_PENDING_CONFIRMATION = 3; protected static final int STATE_AUTHENTICATED = 4; @VisibleForTesting final WakefulnessLifecycle mWakefulnessLifecycle; // 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; Loading @@ -95,25 +113,18 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet private final int mErrorColor; private final float mDialogWidth; protected final DialogViewCallback 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; @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; Loading @@ -126,6 +137,8 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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; Loading @@ -137,6 +150,7 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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 Loading Loading @@ -166,6 +180,30 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet } }; 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) { Loading Loading @@ -346,7 +384,6 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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()); Loading Loading @@ -428,6 +465,42 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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(); Loading @@ -435,6 +508,133 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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 -> { Loading Loading @@ -599,6 +799,12 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet */ @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 */); Loading @@ -619,11 +825,16 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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); } @Override 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); Loading Loading @@ -678,7 +889,28 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet 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() { Loading
packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java +8 −248 File changed.Preview size limit exceeded, changes collapsed. Show changes
packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java +5 −0 Original line number Diff line number Diff line Loading @@ -80,6 +80,11 @@ public class FingerprintDialogView extends BiometricDialogView { } } @Override protected boolean supportsSmallDialog() { return false; } protected boolean shouldAnimateForTransition(int oldState, int newState) { if (newState == STATE_ERROR) { return true; Loading
packages/SystemUI/src/com/android/systemui/biometrics/ui/Utils.java 0 → 100644 +32 −0 Original line number Diff line number Diff line /* * 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.ui; import android.content.Context; import android.util.DisplayMetrics; public class Utils { static float dpToPixels(Context context, float dp) { return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT); } static float pixelsToDp(Context context, float pixels) { return pixels / ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT); } }