Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9d0955d7 authored by Curtis Belmonte's avatar Curtis Belmonte Committed by Android (Google) Code Review
Browse files

Merge "RESTRICT AUTOMERGE Show tip/edge hints for UDFPS enrollment" into sc-dev

parents b4bd7095 1d4c7dc0
Loading
Loading
Loading
Loading
+330 −12
Original line number Diff line number Diff line
@@ -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;
@@ -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;

/**
@@ -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;
@@ -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));
@@ -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) {
@@ -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();
@@ -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
@@ -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
@@ -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
+15 −0
Original line number Diff line number Diff line
@@ -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()) {