Loading packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java +330 −12 Original line number Diff line number Diff line Loading @@ -16,9 +16,11 @@ package com.android.systemui.biometrics; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; Loading @@ -26,11 +28,17 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.util.TypedValue; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.LinearInterpolator; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.graphics.ColorUtils; import com.android.systemui.R; /** Loading @@ -39,10 +47,20 @@ import com.android.systemui.R; public class UdfpsEnrollDrawable extends UdfpsDrawable { private static final String TAG = "UdfpsAnimationEnroll"; private static final long ANIM_DURATION = 800; private static final long HINT_COLOR_ANIM_DELAY_MS = 233L; private static final long HINT_COLOR_ANIM_DURATION_MS = 517L; private static final long HINT_WIDTH_ANIM_DURATION_MS = 233L; private static final long TARGET_ANIM_DURATION_LONG = 800L; private static final long TARGET_ANIM_DURATION_SHORT = 600L; // 1 + SCALE_MAX is the maximum that the moving target will animate to private static final float SCALE_MAX = 0.25f; private static final float HINT_PADDING_DP = 10f; private static final float HINT_MAX_WIDTH_DP = 6f; private static final float HINT_ANGLE = 40f; private final Handler mHandler = new Handler(Looper.getMainLooper()); @NonNull private final Drawable mMovingTargetFpIcon; @NonNull private final Paint mSensorOutlinePaint; @NonNull private final Paint mBlueFill; Loading @@ -51,17 +69,41 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { @Nullable private UdfpsEnrollHelper mEnrollHelper; // Moving target animator set @Nullable AnimatorSet mAnimatorSet; @Nullable AnimatorSet mTargetAnimatorSet; // Moving target location float mCurrentX; float mCurrentY; // Moving target size float mCurrentScale = 1.f; @ColorInt private final int mHintColorFaded; @ColorInt private final int mHintColorHighlight; private final float mHintMaxWidthPx; private final float mHintPaddingPx; @NonNull private final Animator.AnimatorListener mTargetAnimListener; private boolean mShouldShowTipHint = false; @NonNull private final Paint mTipHintPaint; @Nullable private AnimatorSet mTipHintAnimatorSet; @Nullable private ValueAnimator mTipHintColorAnimator; @Nullable private ValueAnimator mTipHintWidthAnimator; @NonNull private final ValueAnimator.AnimatorUpdateListener mTipHintColorUpdateListener; @NonNull private final ValueAnimator.AnimatorUpdateListener mTipHintWidthUpdateListener; @NonNull private final Animator.AnimatorListener mTipHintPulseListener; private boolean mShouldShowEdgeHint = false; @NonNull private final Paint mEdgeHintPaint; @Nullable private AnimatorSet mEdgeHintAnimatorSet; @Nullable private ValueAnimator mEdgeHintColorAnimator; @Nullable private ValueAnimator mEdgeHintWidthAnimator; @NonNull private final ValueAnimator.AnimatorUpdateListener mEdgeHintColorUpdateListener; @NonNull private final ValueAnimator.AnimatorUpdateListener mEdgeHintWidthUpdateListener; @NonNull private final Animator.AnimatorListener mEdgeHintPulseListener; UdfpsEnrollDrawable(@NonNull Context context) { super(context); mSensorOutlinePaint = new Paint(0 /* flags */); mSensorOutlinePaint.setAntiAlias(true); mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_enroll_icon)); Loading @@ -78,6 +120,117 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { mMovingTargetFpIcon.mutate(); mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon)); mHintColorFaded = getHintColorFaded(context); mHintColorHighlight = context.getColor(R.color.udfps_enroll_progress); mHintMaxWidthPx = Utils.dpToPixels(context, HINT_MAX_WIDTH_DP); mHintPaddingPx = Utils.dpToPixels(context, HINT_PADDING_DP); mTargetAnimListener = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) {} @Override public void onAnimationEnd(Animator animation) { updateTipHintVisibility(); } @Override public void onAnimationCancel(Animator animation) {} @Override public void onAnimationRepeat(Animator animation) {} }; mTipHintPaint = new Paint(0 /* flags */); mTipHintPaint.setAntiAlias(true); mTipHintPaint.setColor(mHintColorFaded); mTipHintPaint.setStyle(Paint.Style.STROKE); mTipHintPaint.setStrokeCap(Paint.Cap.ROUND); mTipHintPaint.setStrokeWidth(0f); mTipHintColorUpdateListener = animation -> { mTipHintPaint.setColor((int) animation.getAnimatedValue()); invalidateSelf(); }; mTipHintWidthUpdateListener = animation -> { mTipHintPaint.setStrokeWidth((float) animation.getAnimatedValue()); invalidateSelf(); }; mTipHintPulseListener = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) {} @Override public void onAnimationEnd(Animator animation) { mHandler.postDelayed(() -> { mTipHintColorAnimator = ValueAnimator.ofArgb(mTipHintPaint.getColor(), mHintColorFaded); mTipHintColorAnimator.setInterpolator(new LinearInterpolator()); mTipHintColorAnimator.setDuration(HINT_COLOR_ANIM_DURATION_MS); mTipHintColorAnimator.addUpdateListener(mTipHintColorUpdateListener); mTipHintColorAnimator.start(); }, HINT_COLOR_ANIM_DELAY_MS); } @Override public void onAnimationCancel(Animator animation) {} @Override public void onAnimationRepeat(Animator animation) {} }; mEdgeHintPaint = new Paint(0 /* flags */); mEdgeHintPaint.setAntiAlias(true); mEdgeHintPaint.setColor(mHintColorFaded); mEdgeHintPaint.setStyle(Paint.Style.STROKE); mEdgeHintPaint.setStrokeCap(Paint.Cap.ROUND); mEdgeHintPaint.setStrokeWidth(0f); mEdgeHintColorUpdateListener = animation -> { mEdgeHintPaint.setColor((int) animation.getAnimatedValue()); invalidateSelf(); }; mEdgeHintWidthUpdateListener = animation -> { mEdgeHintPaint.setStrokeWidth((float) animation.getAnimatedValue()); invalidateSelf(); }; mEdgeHintPulseListener = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) {} @Override public void onAnimationEnd(Animator animation) { mHandler.postDelayed(() -> { mEdgeHintColorAnimator = ValueAnimator.ofArgb(mEdgeHintPaint.getColor(), mHintColorFaded); mEdgeHintColorAnimator.setInterpolator(new LinearInterpolator()); mEdgeHintColorAnimator.setDuration(HINT_COLOR_ANIM_DURATION_MS); mEdgeHintColorAnimator.addUpdateListener(mEdgeHintColorUpdateListener); mEdgeHintColorAnimator.start(); }, HINT_COLOR_ANIM_DELAY_MS); } @Override public void onAnimationCancel(Animator animation) {} @Override public void onAnimationRepeat(Animator animation) {} }; } @ColorInt private static int getHintColorFaded(@NonNull Context context) { final TypedValue tv = new TypedValue(); context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, tv, true); final int alpha = (int) (tv.getFloat() * 255f); final int[] attrs = new int[] {android.R.attr.colorControlNormal}; final TypedArray ta = context.obtainStyledAttributes(attrs); try { @ColorInt final int color = ta.getColor(0, context.getColor(R.color.white_disabled)); return ColorUtils.setAlphaComponent(color, alpha); } finally { ta.recycle(); } } void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) { Loading @@ -98,9 +251,13 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { } void onEnrollmentProgress(int remaining, int totalSteps) { if (mEnrollHelper != null && !mEnrollHelper.isCenterEnrollmentStage()) { if (mAnimatorSet != null && mAnimatorSet.isRunning()) { mAnimatorSet.end(); if (mEnrollHelper == null) { return; } if (!mEnrollHelper.isCenterEnrollmentStage()) { if (mTargetAnimatorSet != null && mTargetAnimatorSet.isRunning()) { mTargetAnimatorSet.end(); } final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint(); Loading @@ -117,8 +274,13 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { invalidateSelf(); }); final boolean isMovingToCenter = point.x == 0f && point.y == 0f; final long duration = isMovingToCenter ? TARGET_ANIM_DURATION_SHORT : TARGET_ANIM_DURATION_LONG; final ValueAnimator scale = ValueAnimator.ofFloat(0, (float) Math.PI); scale.setDuration(ANIM_DURATION); scale.setDuration(duration); scale.addUpdateListener(animation -> { // Grow then shrink mCurrentScale = 1 Loading @@ -126,14 +288,117 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { invalidateSelf(); }); mAnimatorSet = new AnimatorSet(); mTargetAnimatorSet = new AnimatorSet(); mAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator()); mAnimatorSet.setDuration(ANIM_DURATION); mAnimatorSet.playTogether(x, y, scale); mAnimatorSet.start(); mTargetAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator()); mTargetAnimatorSet.setDuration(duration); mTargetAnimatorSet.addListener(mTargetAnimListener); mTargetAnimatorSet.playTogether(x, y, scale); mTargetAnimatorSet.start(); } else { updateTipHintVisibility(); } } else { updateTipHintVisibility(); } updateEdgeHintVisibility(); } private void updateTipHintVisibility() { final boolean shouldShow = mEnrollHelper != null && mEnrollHelper.isTipEnrollmentStage(); if (mShouldShowTipHint == shouldShow) { return; } mShouldShowTipHint = shouldShow; if (mTipHintWidthAnimator != null && mTipHintWidthAnimator.isRunning()) { mTipHintWidthAnimator.cancel(); } final float targetWidth = shouldShow ? mHintMaxWidthPx : 0f; mTipHintWidthAnimator = ValueAnimator.ofFloat(mTipHintPaint.getStrokeWidth(), targetWidth); mTipHintWidthAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS); mTipHintWidthAnimator.addUpdateListener(mTipHintWidthUpdateListener); if (shouldShow) { startTipHintPulseAnimation(); } else { mTipHintWidthAnimator.start(); } } private void updateEdgeHintVisibility() { final boolean shouldShow = mEnrollHelper != null && mEnrollHelper.isEdgeEnrollmentStage(); if (mShouldShowEdgeHint == shouldShow) { return; } mShouldShowEdgeHint = shouldShow; if (mEdgeHintWidthAnimator != null && mEdgeHintWidthAnimator.isRunning()) { mEdgeHintWidthAnimator.cancel(); } final float targetWidth = shouldShow ? mHintMaxWidthPx : 0f; mEdgeHintWidthAnimator = ValueAnimator.ofFloat(mEdgeHintPaint.getStrokeWidth(), targetWidth); mEdgeHintWidthAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS); mEdgeHintWidthAnimator.addUpdateListener(mEdgeHintWidthUpdateListener); if (shouldShow) { startEdgeHintPulseAnimation(); } else { mEdgeHintWidthAnimator.start(); } } private void startTipHintPulseAnimation() { mHandler.removeCallbacksAndMessages(null); if (mTipHintAnimatorSet != null && mTipHintAnimatorSet.isRunning()) { mTipHintAnimatorSet.cancel(); } if (mTipHintColorAnimator != null && mTipHintColorAnimator.isRunning()) { mTipHintColorAnimator.cancel(); } mTipHintColorAnimator = ValueAnimator.ofArgb(mTipHintPaint.getColor(), mHintColorHighlight); mTipHintColorAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS); mTipHintColorAnimator.addUpdateListener(mTipHintColorUpdateListener); mTipHintColorAnimator.addListener(mTipHintPulseListener); mTipHintAnimatorSet = new AnimatorSet(); mTipHintAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator()); mTipHintAnimatorSet.playTogether(mTipHintColorAnimator, mTipHintWidthAnimator); mTipHintAnimatorSet.start(); } private void startEdgeHintPulseAnimation() { mHandler.removeCallbacksAndMessages(null); if (mEdgeHintAnimatorSet != null && mEdgeHintAnimatorSet.isRunning()) { mEdgeHintAnimatorSet.cancel(); } if (mEdgeHintColorAnimator != null && mEdgeHintColorAnimator.isRunning()) { mEdgeHintColorAnimator.cancel(); } mEdgeHintColorAnimator = ValueAnimator.ofArgb(mEdgeHintPaint.getColor(), mHintColorHighlight); mEdgeHintColorAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS); mEdgeHintColorAnimator.addUpdateListener(mEdgeHintColorUpdateListener); mEdgeHintColorAnimator.addListener(mEdgeHintPulseListener); mEdgeHintAnimatorSet = new AnimatorSet(); mEdgeHintAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator()); mEdgeHintAnimatorSet.playTogether(mEdgeHintColorAnimator, mEdgeHintWidthAnimator); mEdgeHintAnimatorSet.start(); } private boolean isTipHintVisible() { return mTipHintPaint.getStrokeWidth() > 0f; } private boolean isEdgeHintVisible() { return mEdgeHintPaint.getStrokeWidth() > 0f; } @Override Loading Loading @@ -163,6 +428,59 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { mFingerprintDrawable.setAlpha(mAlpha); mSensorOutlinePaint.setAlpha(mAlpha); } // Draw the finger tip or edges hint. if (isTipHintVisible() || isEdgeHintVisible()) { canvas.save(); // Make arcs start from the top, rather than the right. canvas.rotate(-90f, mSensorRect.centerX(), mSensorRect.centerY()); final float halfSensorHeight = Math.abs(mSensorRect.bottom - mSensorRect.top) / 2f; final float halfSensorWidth = Math.abs(mSensorRect.right - mSensorRect.left) / 2f; final float hintXOffset = halfSensorWidth + mHintPaddingPx; final float hintYOffset = halfSensorHeight + mHintPaddingPx; if (isTipHintVisible()) { canvas.drawArc( mSensorRect.centerX() - hintXOffset, mSensorRect.centerY() - hintYOffset, mSensorRect.centerX() + hintXOffset, mSensorRect.centerY() + hintYOffset, -HINT_ANGLE / 2f, HINT_ANGLE, false /* useCenter */, mTipHintPaint); } if (isEdgeHintVisible()) { // Draw right edge hint. canvas.rotate(-90f, mSensorRect.centerX(), mSensorRect.centerY()); canvas.drawArc( mSensorRect.centerX() - hintXOffset, mSensorRect.centerY() - hintYOffset, mSensorRect.centerX() + hintXOffset, mSensorRect.centerY() + hintYOffset, -HINT_ANGLE / 2f, HINT_ANGLE, false /* useCenter */, mEdgeHintPaint); // Draw left edge hint. canvas.rotate(180f, mSensorRect.centerX(), mSensorRect.centerY()); canvas.drawArc( mSensorRect.centerX() - hintXOffset, mSensorRect.centerY() - hintYOffset, mSensorRect.centerX() + hintXOffset, mSensorRect.centerY() + hintYOffset, -HINT_ANGLE / 2f, HINT_ANGLE, false /* useCenter */, mEdgeHintPaint); } canvas.restore(); } } @Override Loading packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java +15 −0 Original line number Diff line number Diff line Loading @@ -202,6 +202,21 @@ public class UdfpsEnrollHelper { return progressSteps >= STAGE_THRESHOLDS[0] && progressSteps < STAGE_THRESHOLDS[1]; } boolean isTipEnrollmentStage() { if (mTotalSteps == -1 || mRemainingSteps == -1) { return false; } final int progressSteps = mTotalSteps - mRemainingSteps; return progressSteps >= STAGE_THRESHOLDS[1] && progressSteps < STAGE_THRESHOLDS[2]; } boolean isEdgeEnrollmentStage() { if (mTotalSteps == -1 || mRemainingSteps == -1) { return false; } return mTotalSteps - mRemainingSteps >= STAGE_THRESHOLDS[2]; } @NonNull PointF getNextGuidedEnrollmentPoint() { if (mAccessibilityEnabled || !isGuidedEnrollmentStage()) { Loading Loading
packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java +330 −12 Original line number Diff line number Diff line Loading @@ -16,9 +16,11 @@ package com.android.systemui.biometrics; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; Loading @@ -26,11 +28,17 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.util.TypedValue; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.LinearInterpolator; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.graphics.ColorUtils; import com.android.systemui.R; /** Loading @@ -39,10 +47,20 @@ import com.android.systemui.R; public class UdfpsEnrollDrawable extends UdfpsDrawable { private static final String TAG = "UdfpsAnimationEnroll"; private static final long ANIM_DURATION = 800; private static final long HINT_COLOR_ANIM_DELAY_MS = 233L; private static final long HINT_COLOR_ANIM_DURATION_MS = 517L; private static final long HINT_WIDTH_ANIM_DURATION_MS = 233L; private static final long TARGET_ANIM_DURATION_LONG = 800L; private static final long TARGET_ANIM_DURATION_SHORT = 600L; // 1 + SCALE_MAX is the maximum that the moving target will animate to private static final float SCALE_MAX = 0.25f; private static final float HINT_PADDING_DP = 10f; private static final float HINT_MAX_WIDTH_DP = 6f; private static final float HINT_ANGLE = 40f; private final Handler mHandler = new Handler(Looper.getMainLooper()); @NonNull private final Drawable mMovingTargetFpIcon; @NonNull private final Paint mSensorOutlinePaint; @NonNull private final Paint mBlueFill; Loading @@ -51,17 +69,41 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { @Nullable private UdfpsEnrollHelper mEnrollHelper; // Moving target animator set @Nullable AnimatorSet mAnimatorSet; @Nullable AnimatorSet mTargetAnimatorSet; // Moving target location float mCurrentX; float mCurrentY; // Moving target size float mCurrentScale = 1.f; @ColorInt private final int mHintColorFaded; @ColorInt private final int mHintColorHighlight; private final float mHintMaxWidthPx; private final float mHintPaddingPx; @NonNull private final Animator.AnimatorListener mTargetAnimListener; private boolean mShouldShowTipHint = false; @NonNull private final Paint mTipHintPaint; @Nullable private AnimatorSet mTipHintAnimatorSet; @Nullable private ValueAnimator mTipHintColorAnimator; @Nullable private ValueAnimator mTipHintWidthAnimator; @NonNull private final ValueAnimator.AnimatorUpdateListener mTipHintColorUpdateListener; @NonNull private final ValueAnimator.AnimatorUpdateListener mTipHintWidthUpdateListener; @NonNull private final Animator.AnimatorListener mTipHintPulseListener; private boolean mShouldShowEdgeHint = false; @NonNull private final Paint mEdgeHintPaint; @Nullable private AnimatorSet mEdgeHintAnimatorSet; @Nullable private ValueAnimator mEdgeHintColorAnimator; @Nullable private ValueAnimator mEdgeHintWidthAnimator; @NonNull private final ValueAnimator.AnimatorUpdateListener mEdgeHintColorUpdateListener; @NonNull private final ValueAnimator.AnimatorUpdateListener mEdgeHintWidthUpdateListener; @NonNull private final Animator.AnimatorListener mEdgeHintPulseListener; UdfpsEnrollDrawable(@NonNull Context context) { super(context); mSensorOutlinePaint = new Paint(0 /* flags */); mSensorOutlinePaint.setAntiAlias(true); mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_enroll_icon)); Loading @@ -78,6 +120,117 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { mMovingTargetFpIcon.mutate(); mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon)); mHintColorFaded = getHintColorFaded(context); mHintColorHighlight = context.getColor(R.color.udfps_enroll_progress); mHintMaxWidthPx = Utils.dpToPixels(context, HINT_MAX_WIDTH_DP); mHintPaddingPx = Utils.dpToPixels(context, HINT_PADDING_DP); mTargetAnimListener = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) {} @Override public void onAnimationEnd(Animator animation) { updateTipHintVisibility(); } @Override public void onAnimationCancel(Animator animation) {} @Override public void onAnimationRepeat(Animator animation) {} }; mTipHintPaint = new Paint(0 /* flags */); mTipHintPaint.setAntiAlias(true); mTipHintPaint.setColor(mHintColorFaded); mTipHintPaint.setStyle(Paint.Style.STROKE); mTipHintPaint.setStrokeCap(Paint.Cap.ROUND); mTipHintPaint.setStrokeWidth(0f); mTipHintColorUpdateListener = animation -> { mTipHintPaint.setColor((int) animation.getAnimatedValue()); invalidateSelf(); }; mTipHintWidthUpdateListener = animation -> { mTipHintPaint.setStrokeWidth((float) animation.getAnimatedValue()); invalidateSelf(); }; mTipHintPulseListener = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) {} @Override public void onAnimationEnd(Animator animation) { mHandler.postDelayed(() -> { mTipHintColorAnimator = ValueAnimator.ofArgb(mTipHintPaint.getColor(), mHintColorFaded); mTipHintColorAnimator.setInterpolator(new LinearInterpolator()); mTipHintColorAnimator.setDuration(HINT_COLOR_ANIM_DURATION_MS); mTipHintColorAnimator.addUpdateListener(mTipHintColorUpdateListener); mTipHintColorAnimator.start(); }, HINT_COLOR_ANIM_DELAY_MS); } @Override public void onAnimationCancel(Animator animation) {} @Override public void onAnimationRepeat(Animator animation) {} }; mEdgeHintPaint = new Paint(0 /* flags */); mEdgeHintPaint.setAntiAlias(true); mEdgeHintPaint.setColor(mHintColorFaded); mEdgeHintPaint.setStyle(Paint.Style.STROKE); mEdgeHintPaint.setStrokeCap(Paint.Cap.ROUND); mEdgeHintPaint.setStrokeWidth(0f); mEdgeHintColorUpdateListener = animation -> { mEdgeHintPaint.setColor((int) animation.getAnimatedValue()); invalidateSelf(); }; mEdgeHintWidthUpdateListener = animation -> { mEdgeHintPaint.setStrokeWidth((float) animation.getAnimatedValue()); invalidateSelf(); }; mEdgeHintPulseListener = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) {} @Override public void onAnimationEnd(Animator animation) { mHandler.postDelayed(() -> { mEdgeHintColorAnimator = ValueAnimator.ofArgb(mEdgeHintPaint.getColor(), mHintColorFaded); mEdgeHintColorAnimator.setInterpolator(new LinearInterpolator()); mEdgeHintColorAnimator.setDuration(HINT_COLOR_ANIM_DURATION_MS); mEdgeHintColorAnimator.addUpdateListener(mEdgeHintColorUpdateListener); mEdgeHintColorAnimator.start(); }, HINT_COLOR_ANIM_DELAY_MS); } @Override public void onAnimationCancel(Animator animation) {} @Override public void onAnimationRepeat(Animator animation) {} }; } @ColorInt private static int getHintColorFaded(@NonNull Context context) { final TypedValue tv = new TypedValue(); context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, tv, true); final int alpha = (int) (tv.getFloat() * 255f); final int[] attrs = new int[] {android.R.attr.colorControlNormal}; final TypedArray ta = context.obtainStyledAttributes(attrs); try { @ColorInt final int color = ta.getColor(0, context.getColor(R.color.white_disabled)); return ColorUtils.setAlphaComponent(color, alpha); } finally { ta.recycle(); } } void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) { Loading @@ -98,9 +251,13 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { } void onEnrollmentProgress(int remaining, int totalSteps) { if (mEnrollHelper != null && !mEnrollHelper.isCenterEnrollmentStage()) { if (mAnimatorSet != null && mAnimatorSet.isRunning()) { mAnimatorSet.end(); if (mEnrollHelper == null) { return; } if (!mEnrollHelper.isCenterEnrollmentStage()) { if (mTargetAnimatorSet != null && mTargetAnimatorSet.isRunning()) { mTargetAnimatorSet.end(); } final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint(); Loading @@ -117,8 +274,13 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { invalidateSelf(); }); final boolean isMovingToCenter = point.x == 0f && point.y == 0f; final long duration = isMovingToCenter ? TARGET_ANIM_DURATION_SHORT : TARGET_ANIM_DURATION_LONG; final ValueAnimator scale = ValueAnimator.ofFloat(0, (float) Math.PI); scale.setDuration(ANIM_DURATION); scale.setDuration(duration); scale.addUpdateListener(animation -> { // Grow then shrink mCurrentScale = 1 Loading @@ -126,14 +288,117 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { invalidateSelf(); }); mAnimatorSet = new AnimatorSet(); mTargetAnimatorSet = new AnimatorSet(); mAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator()); mAnimatorSet.setDuration(ANIM_DURATION); mAnimatorSet.playTogether(x, y, scale); mAnimatorSet.start(); mTargetAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator()); mTargetAnimatorSet.setDuration(duration); mTargetAnimatorSet.addListener(mTargetAnimListener); mTargetAnimatorSet.playTogether(x, y, scale); mTargetAnimatorSet.start(); } else { updateTipHintVisibility(); } } else { updateTipHintVisibility(); } updateEdgeHintVisibility(); } private void updateTipHintVisibility() { final boolean shouldShow = mEnrollHelper != null && mEnrollHelper.isTipEnrollmentStage(); if (mShouldShowTipHint == shouldShow) { return; } mShouldShowTipHint = shouldShow; if (mTipHintWidthAnimator != null && mTipHintWidthAnimator.isRunning()) { mTipHintWidthAnimator.cancel(); } final float targetWidth = shouldShow ? mHintMaxWidthPx : 0f; mTipHintWidthAnimator = ValueAnimator.ofFloat(mTipHintPaint.getStrokeWidth(), targetWidth); mTipHintWidthAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS); mTipHintWidthAnimator.addUpdateListener(mTipHintWidthUpdateListener); if (shouldShow) { startTipHintPulseAnimation(); } else { mTipHintWidthAnimator.start(); } } private void updateEdgeHintVisibility() { final boolean shouldShow = mEnrollHelper != null && mEnrollHelper.isEdgeEnrollmentStage(); if (mShouldShowEdgeHint == shouldShow) { return; } mShouldShowEdgeHint = shouldShow; if (mEdgeHintWidthAnimator != null && mEdgeHintWidthAnimator.isRunning()) { mEdgeHintWidthAnimator.cancel(); } final float targetWidth = shouldShow ? mHintMaxWidthPx : 0f; mEdgeHintWidthAnimator = ValueAnimator.ofFloat(mEdgeHintPaint.getStrokeWidth(), targetWidth); mEdgeHintWidthAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS); mEdgeHintWidthAnimator.addUpdateListener(mEdgeHintWidthUpdateListener); if (shouldShow) { startEdgeHintPulseAnimation(); } else { mEdgeHintWidthAnimator.start(); } } private void startTipHintPulseAnimation() { mHandler.removeCallbacksAndMessages(null); if (mTipHintAnimatorSet != null && mTipHintAnimatorSet.isRunning()) { mTipHintAnimatorSet.cancel(); } if (mTipHintColorAnimator != null && mTipHintColorAnimator.isRunning()) { mTipHintColorAnimator.cancel(); } mTipHintColorAnimator = ValueAnimator.ofArgb(mTipHintPaint.getColor(), mHintColorHighlight); mTipHintColorAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS); mTipHintColorAnimator.addUpdateListener(mTipHintColorUpdateListener); mTipHintColorAnimator.addListener(mTipHintPulseListener); mTipHintAnimatorSet = new AnimatorSet(); mTipHintAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator()); mTipHintAnimatorSet.playTogether(mTipHintColorAnimator, mTipHintWidthAnimator); mTipHintAnimatorSet.start(); } private void startEdgeHintPulseAnimation() { mHandler.removeCallbacksAndMessages(null); if (mEdgeHintAnimatorSet != null && mEdgeHintAnimatorSet.isRunning()) { mEdgeHintAnimatorSet.cancel(); } if (mEdgeHintColorAnimator != null && mEdgeHintColorAnimator.isRunning()) { mEdgeHintColorAnimator.cancel(); } mEdgeHintColorAnimator = ValueAnimator.ofArgb(mEdgeHintPaint.getColor(), mHintColorHighlight); mEdgeHintColorAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS); mEdgeHintColorAnimator.addUpdateListener(mEdgeHintColorUpdateListener); mEdgeHintColorAnimator.addListener(mEdgeHintPulseListener); mEdgeHintAnimatorSet = new AnimatorSet(); mEdgeHintAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator()); mEdgeHintAnimatorSet.playTogether(mEdgeHintColorAnimator, mEdgeHintWidthAnimator); mEdgeHintAnimatorSet.start(); } private boolean isTipHintVisible() { return mTipHintPaint.getStrokeWidth() > 0f; } private boolean isEdgeHintVisible() { return mEdgeHintPaint.getStrokeWidth() > 0f; } @Override Loading Loading @@ -163,6 +428,59 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { mFingerprintDrawable.setAlpha(mAlpha); mSensorOutlinePaint.setAlpha(mAlpha); } // Draw the finger tip or edges hint. if (isTipHintVisible() || isEdgeHintVisible()) { canvas.save(); // Make arcs start from the top, rather than the right. canvas.rotate(-90f, mSensorRect.centerX(), mSensorRect.centerY()); final float halfSensorHeight = Math.abs(mSensorRect.bottom - mSensorRect.top) / 2f; final float halfSensorWidth = Math.abs(mSensorRect.right - mSensorRect.left) / 2f; final float hintXOffset = halfSensorWidth + mHintPaddingPx; final float hintYOffset = halfSensorHeight + mHintPaddingPx; if (isTipHintVisible()) { canvas.drawArc( mSensorRect.centerX() - hintXOffset, mSensorRect.centerY() - hintYOffset, mSensorRect.centerX() + hintXOffset, mSensorRect.centerY() + hintYOffset, -HINT_ANGLE / 2f, HINT_ANGLE, false /* useCenter */, mTipHintPaint); } if (isEdgeHintVisible()) { // Draw right edge hint. canvas.rotate(-90f, mSensorRect.centerX(), mSensorRect.centerY()); canvas.drawArc( mSensorRect.centerX() - hintXOffset, mSensorRect.centerY() - hintYOffset, mSensorRect.centerX() + hintXOffset, mSensorRect.centerY() + hintYOffset, -HINT_ANGLE / 2f, HINT_ANGLE, false /* useCenter */, mEdgeHintPaint); // Draw left edge hint. canvas.rotate(180f, mSensorRect.centerX(), mSensorRect.centerY()); canvas.drawArc( mSensorRect.centerX() - hintXOffset, mSensorRect.centerY() - hintYOffset, mSensorRect.centerX() + hintXOffset, mSensorRect.centerY() + hintYOffset, -HINT_ANGLE / 2f, HINT_ANGLE, false /* useCenter */, mEdgeHintPaint); } canvas.restore(); } } @Override Loading
packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java +15 −0 Original line number Diff line number Diff line Loading @@ -202,6 +202,21 @@ public class UdfpsEnrollHelper { return progressSteps >= STAGE_THRESHOLDS[0] && progressSteps < STAGE_THRESHOLDS[1]; } boolean isTipEnrollmentStage() { if (mTotalSteps == -1 || mRemainingSteps == -1) { return false; } final int progressSteps = mTotalSteps - mRemainingSteps; return progressSteps >= STAGE_THRESHOLDS[1] && progressSteps < STAGE_THRESHOLDS[2]; } boolean isEdgeEnrollmentStage() { if (mTotalSteps == -1 || mRemainingSteps == -1) { return false; } return mTotalSteps - mRemainingSteps >= STAGE_THRESHOLDS[2]; } @NonNull PointF getNextGuidedEnrollmentPoint() { if (mAccessibilityEnabled || !isGuidedEnrollmentStage()) { Loading