Merge "RESTRICT AUTOMERGE Support UDFPS multi-stage enrollment" into sc-dev

This commit is contained in:
Curtis Belmonte
2021-09-07 04:32:55 +00:00
committed by Android (Google) Code Review
6 changed files with 402 additions and 137 deletions

View File

@@ -210,6 +210,7 @@ public class UdfpsController implements DozeReceiver {
} }
void onAcquiredGood() { void onAcquiredGood() {
Log.d(TAG, "onAcquiredGood");
if (mEnrollHelper != null) { if (mEnrollHelper != null) {
mEnrollHelper.animateIfLastStep(); mEnrollHelper.animateIfLastStep();
} }

View File

@@ -98,40 +98,41 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
} }
void onEnrollmentProgress(int remaining, int totalSteps) { void onEnrollmentProgress(int remaining, int totalSteps) {
if (mEnrollHelper.isCenterEnrollmentComplete()) { if (mEnrollHelper != null && !mEnrollHelper.isCenterEnrollmentStage()) {
if (mAnimatorSet != null && mAnimatorSet.isRunning()) { if (mAnimatorSet != null && mAnimatorSet.isRunning()) {
mAnimatorSet.end(); mAnimatorSet.end();
} }
final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint(); final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint();
if (mCurrentX != point.x || mCurrentY != point.y) {
final ValueAnimator x = ValueAnimator.ofFloat(mCurrentX, point.x);
x.addUpdateListener(animation -> {
mCurrentX = (float) animation.getAnimatedValue();
invalidateSelf();
});
final ValueAnimator x = ValueAnimator.ofFloat(mCurrentX, point.x); final ValueAnimator y = ValueAnimator.ofFloat(mCurrentY, point.y);
x.addUpdateListener(animation -> { y.addUpdateListener(animation -> {
mCurrentX = (float) animation.getAnimatedValue(); mCurrentY = (float) animation.getAnimatedValue();
invalidateSelf(); invalidateSelf();
}); });
final ValueAnimator y = ValueAnimator.ofFloat(mCurrentY, point.y); final ValueAnimator scale = ValueAnimator.ofFloat(0, (float) Math.PI);
y.addUpdateListener(animation -> { scale.setDuration(ANIM_DURATION);
mCurrentY = (float) animation.getAnimatedValue(); scale.addUpdateListener(animation -> {
invalidateSelf(); // Grow then shrink
}); mCurrentScale = 1
+ SCALE_MAX * (float) Math.sin((float) animation.getAnimatedValue());
invalidateSelf();
});
final ValueAnimator scale = ValueAnimator.ofFloat(0, (float) Math.PI); mAnimatorSet = new AnimatorSet();
scale.setDuration(ANIM_DURATION);
scale.addUpdateListener(animation -> {
// Grow then shrink
mCurrentScale = 1 +
SCALE_MAX * (float) Math.sin((float) animation.getAnimatedValue());
invalidateSelf();
});
mAnimatorSet = new AnimatorSet(); mAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
mAnimatorSet.setDuration(ANIM_DURATION);
mAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator()); mAnimatorSet.playTogether(x, y, scale);
mAnimatorSet.setDuration(ANIM_DURATION); mAnimatorSet.start();
mAnimatorSet.playTogether(x, y, scale); }
mAnimatorSet.start();
} }
} }
@@ -142,7 +143,7 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
} }
// Draw moving target // Draw moving target
if (mEnrollHelper.isCenterEnrollmentComplete()) { if (mEnrollHelper != null && !mEnrollHelper.isCenterEnrollmentStage()) {
canvas.save(); canvas.save();
canvas.translate(mCurrentX, mCurrentY); canvas.translate(mCurrentX, mCurrentY);

View File

@@ -44,8 +44,15 @@ public class UdfpsEnrollHelper {
private static final String NEW_COORDS_OVERRIDE = private static final String NEW_COORDS_OVERRIDE =
"com.android.systemui.biometrics.UdfpsNewCoords"; "com.android.systemui.biometrics.UdfpsNewCoords";
// Enroll with two center touches before going to guided enrollment static final int ENROLL_STAGE_COUNT = 4;
private static final int NUM_CENTER_TOUCHES = 2;
// TODO(b/198928407): Consolidate with FingerprintEnrollEnrolling
private static final int[] STAGE_THRESHOLDS = new int[] {
2, // center
18, // guided
22, // fingertip
38, // edges
};
interface Listener { interface Listener {
void onEnrollmentProgress(int remaining, int totalSteps); void onEnrollmentProgress(int remaining, int totalSteps);
@@ -65,6 +72,8 @@ public class UdfpsEnrollHelper {
// interface makes no promises about monotonically increasing by one each time. // interface makes no promises about monotonically increasing by one each time.
private int mLocationsEnrolled = 0; private int mLocationsEnrolled = 0;
private int mCenterTouchCount = 0;
@Nullable Listener mListener; @Nullable Listener mListener;
public UdfpsEnrollHelper(@NonNull Context context, int reason) { public UdfpsEnrollHelper(@NonNull Context context, int reason) {
@@ -117,17 +126,43 @@ public class UdfpsEnrollHelper {
} }
} }
static int getStageThreshold(int index) {
return STAGE_THRESHOLDS[index];
}
static int getLastStageThreshold() {
return STAGE_THRESHOLDS[ENROLL_STAGE_COUNT - 1];
}
boolean shouldShowProgressBar() { boolean shouldShowProgressBar() {
return mEnrollReason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING; return mEnrollReason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING;
} }
void onEnrollmentProgress(int remaining) { void onEnrollmentProgress(int remaining) {
if (mTotalSteps == -1) { Log.d(TAG, "onEnrollmentProgress: remaining = " + remaining
mTotalSteps = remaining; + ", mRemainingSteps = " + mRemainingSteps
} + ", mTotalSteps = " + mTotalSteps
+ ", mLocationsEnrolled = " + mLocationsEnrolled
+ ", mCenterTouchCount = " + mCenterTouchCount);
if (remaining != mRemainingSteps) { if (remaining != mRemainingSteps) {
mLocationsEnrolled++; mLocationsEnrolled++;
if (isCenterEnrollmentStage()) {
mCenterTouchCount++;
}
}
if (mTotalSteps == -1) {
mTotalSteps = remaining;
// Allocate (or subtract) any extra steps for the first enroll stage.
final int extraSteps = mTotalSteps - getLastStageThreshold();
if (extraSteps != 0) {
for (int stageIndex = 0; stageIndex < ENROLL_STAGE_COUNT; stageIndex++) {
STAGE_THRESHOLDS[stageIndex] =
Math.max(0, STAGE_THRESHOLDS[stageIndex] + extraSteps);
}
}
} }
mRemainingSteps = remaining; mRemainingSteps = remaining;
@@ -152,19 +187,24 @@ public class UdfpsEnrollHelper {
} }
} }
boolean isCenterEnrollmentComplete() { boolean isCenterEnrollmentStage() {
if (mTotalSteps == -1 || mRemainingSteps == -1) { if (mTotalSteps == -1 || mRemainingSteps == -1) {
return false; return true;
} else if (mAccessibilityEnabled) { }
return mTotalSteps - mRemainingSteps < STAGE_THRESHOLDS[0];
}
boolean isGuidedEnrollmentStage() {
if (mAccessibilityEnabled || mTotalSteps == -1 || mRemainingSteps == -1) {
return false; return false;
} }
final int stepsEnrolled = mTotalSteps - mRemainingSteps; final int progressSteps = mTotalSteps - mRemainingSteps;
return stepsEnrolled >= NUM_CENTER_TOUCHES; return progressSteps >= STAGE_THRESHOLDS[0] && progressSteps < STAGE_THRESHOLDS[1];
} }
@NonNull @NonNull
PointF getNextGuidedEnrollmentPoint() { PointF getNextGuidedEnrollmentPoint() {
if (mAccessibilityEnabled) { if (mAccessibilityEnabled || !isGuidedEnrollmentStage()) {
return new PointF(0f, 0f); return new PointF(0f, 0f);
} }
@@ -174,13 +214,14 @@ public class UdfpsEnrollHelper {
SCALE_OVERRIDE, SCALE, SCALE_OVERRIDE, SCALE,
UserHandle.USER_CURRENT); UserHandle.USER_CURRENT);
} }
final int index = mLocationsEnrolled - NUM_CENTER_TOUCHES; final int index = mLocationsEnrolled - mCenterTouchCount;
final PointF originalPoint = mGuidedEnrollmentPoints final PointF originalPoint = mGuidedEnrollmentPoints
.get(index % mGuidedEnrollmentPoints.size()); .get(index % mGuidedEnrollmentPoints.size());
return new PointF(originalPoint.x * scale, originalPoint.y * scale); return new PointF(originalPoint.x * scale, originalPoint.y * scale);
} }
void animateIfLastStep() { void animateIfLastStep() {
Log.d(TAG, "animateIfLastStep: mRemainingSteps = " + mRemainingSteps);
if (mListener == null) { if (mListener == null) {
Log.e(TAG, "animateIfLastStep, null listener"); Log.e(TAG, "animateIfLastStep, null listener");
return; return;

View File

@@ -16,141 +16,107 @@
package com.android.systemui.biometrics; package com.android.systemui.biometrics;
import android.animation.ValueAnimator;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.ColorFilter; import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.util.Log; import android.util.Log;
import android.util.TypedValue;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.systemui.R; import java.util.ArrayList;
import java.util.List;
/** /**
* UDFPS enrollment progress bar. * UDFPS enrollment progress bar.
*/ */
public class UdfpsEnrollProgressBarDrawable extends Drawable { public class UdfpsEnrollProgressBarDrawable extends Drawable {
private static final String TAG = "UdfpsProgressBar";
private static final String TAG = "UdfpsEnrollProgressBarDrawable"; private static final float SEGMENT_GAP_ANGLE = 12f;
private static final float PROGRESS_BAR_THICKNESS_DP = 12; @NonNull private final List<UdfpsEnrollProgressBarSegment> mSegments;
@NonNull private final Context mContext;
@NonNull private final Paint mBackgroundCirclePaint;
@NonNull private final Paint mProgressPaint;
@Nullable private ValueAnimator mProgressAnimator;
private float mProgress;
private int mRotation; // After last step, rotate the progress bar once
private boolean mLastStepAcquired;
public UdfpsEnrollProgressBarDrawable(@NonNull Context context) { public UdfpsEnrollProgressBarDrawable(@NonNull Context context) {
mContext = context; mSegments = new ArrayList<>(UdfpsEnrollHelper.ENROLL_STAGE_COUNT);
float startAngle = SEGMENT_GAP_ANGLE / 2f;
mBackgroundCirclePaint = new Paint(); final float sweepAngle = (360f / UdfpsEnrollHelper.ENROLL_STAGE_COUNT) - SEGMENT_GAP_ANGLE;
mBackgroundCirclePaint.setStrokeWidth(Utils.dpToPixels(context, PROGRESS_BAR_THICKNESS_DP)); final Runnable invalidateRunnable = this::invalidateSelf;
mBackgroundCirclePaint.setColor(context.getColor(R.color.white_disabled)); for (int index = 0; index < UdfpsEnrollHelper.ENROLL_STAGE_COUNT; index++) {
mBackgroundCirclePaint.setAntiAlias(true); mSegments.add(new UdfpsEnrollProgressBarSegment(context, getBounds(), startAngle,
mBackgroundCirclePaint.setStyle(Paint.Style.STROKE); sweepAngle, SEGMENT_GAP_ANGLE, invalidateRunnable));
startAngle += sweepAngle + SEGMENT_GAP_ANGLE;
// Background circle color + alpha }
TypedArray tc = context.obtainStyledAttributes(
new int[] {android.R.attr.colorControlNormal});
int tintColor = tc.getColor(0, mBackgroundCirclePaint.getColor());
mBackgroundCirclePaint.setColor(tintColor);
tc.recycle();
TypedValue alpha = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, alpha, true);
mBackgroundCirclePaint.setAlpha((int) (alpha.getFloat() * 255));
// Progress should not be color extracted
mProgressPaint = new Paint();
mProgressPaint.setStrokeWidth(Utils.dpToPixels(context, PROGRESS_BAR_THICKNESS_DP));
mProgressPaint.setColor(context.getColor(R.color.udfps_enroll_progress));
mProgressPaint.setAntiAlias(true);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
} }
void setEnrollmentProgress(int remaining, int totalSteps) { void setEnrollmentProgress(int remaining, int totalSteps) {
// Add one so that the first steps actually changes progress, but also so that the last if (remaining == totalSteps) {
// step ends at 1.0 // Show some progress for the initial touch.
final float progress = (totalSteps - remaining + 1) / (float) (totalSteps + 1); setEnrollmentProgress(1);
setEnrollmentProgress(progress); } else {
setEnrollmentProgress(totalSteps - remaining);
}
} }
private void setEnrollmentProgress(float progress) { private void setEnrollmentProgress(int progressSteps) {
if (mLastStepAcquired) { Log.d(TAG, "setEnrollmentProgress: progressSteps = " + progressSteps);
return;
int segmentIndex = 0;
int prevThreshold = 0;
while (segmentIndex < mSegments.size()) {
final UdfpsEnrollProgressBarSegment segment = mSegments.get(segmentIndex);
final int threshold = UdfpsEnrollHelper.getStageThreshold(segmentIndex);
if (progressSteps >= threshold && !segment.isFilledOrFilling()) {
Log.d(TAG, "setEnrollmentProgress: segment[" + segmentIndex + "] complete");
segment.updateProgress(1f);
break;
} else if (progressSteps >= prevThreshold && progressSteps < threshold) {
final int relativeSteps = progressSteps - prevThreshold;
final int relativeThreshold = threshold - prevThreshold;
final float segmentProgress = (float) relativeSteps / (float) relativeThreshold;
Log.d(TAG, "setEnrollmentProgress: segment[" + segmentIndex + "] progress = "
+ segmentProgress);
segment.updateProgress(segmentProgress);
break;
}
segmentIndex++;
prevThreshold = threshold;
} }
long animationDuration = 150; if (progressSteps >= UdfpsEnrollHelper.getLastStageThreshold()) {
Log.d(TAG, "setEnrollmentProgress: startCompletionAnimation");
if (progress == 1.f) { for (final UdfpsEnrollProgressBarSegment segment : mSegments) {
animationDuration = 400; segment.startCompletionAnimation();
final ValueAnimator rotationAnimator = ValueAnimator.ofInt(0, 400); }
rotationAnimator.setDuration(animationDuration); } else {
rotationAnimator.addUpdateListener(animation -> { Log.d(TAG, "setEnrollmentProgress: cancelCompletionAnimation");
Log.d(TAG, "Rotation: " + mRotation); for (final UdfpsEnrollProgressBarSegment segment : mSegments) {
mRotation = (int) animation.getAnimatedValue(); segment.cancelCompletionAnimation();
invalidateSelf(); }
});
rotationAnimator.start();
} }
if (mProgressAnimator != null && mProgressAnimator.isRunning()) {
mProgressAnimator.cancel();
}
mProgressAnimator = ValueAnimator.ofFloat(mProgress, progress);
mProgressAnimator.setDuration(animationDuration);
mProgressAnimator.addUpdateListener(animation -> {
mProgress = (float) animation.getAnimatedValue();
invalidateSelf();
});
mProgressAnimator.start();
} }
void onLastStepAcquired() { void onLastStepAcquired() {
setEnrollmentProgress(1.f); Log.d(TAG, "setEnrollmentProgress: onLastStepAcquired");
mLastStepAcquired = true; setEnrollmentProgress(UdfpsEnrollHelper.getLastStageThreshold());
} }
@Override @Override
public void draw(@NonNull Canvas canvas) { public void draw(@NonNull Canvas canvas) {
Log.d(TAG, "setEnrollmentProgress: draw");
canvas.save(); canvas.save();
// Progress starts from the top, instead of the right // Progress starts from the top, instead of the right
canvas.rotate(-90 + mRotation, getBounds().centerX(), getBounds().centerY()); canvas.rotate(-90f, getBounds().centerX(), getBounds().centerY());
// Progress bar "background track" // Draw each of the enroll segments.
final float halfPaddingPx = Utils.dpToPixels(mContext, PROGRESS_BAR_THICKNESS_DP) / 2; for (final UdfpsEnrollProgressBarSegment segment : mSegments) {
canvas.drawArc(halfPaddingPx, segment.draw(canvas);
halfPaddingPx, }
getBounds().right - halfPaddingPx,
getBounds().bottom - halfPaddingPx,
0,
360,
false,
mBackgroundCirclePaint
);
final float progress = 360.f * mProgress;
// Progress
canvas.drawArc(halfPaddingPx,
halfPaddingPx,
getBounds().right - halfPaddingPx,
getBounds().bottom - halfPaddingPx,
0,
progress,
false,
mProgressPaint
);
canvas.restore(); canvas.restore();
} }

View File

@@ -0,0 +1,256 @@
/*
* Copyright (C) 2021 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.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.util.TypedValue;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.R;
/**
* A single segment of the UDFPS enrollment progress bar.
*/
public class UdfpsEnrollProgressBarSegment {
private static final String TAG = "UdfpsProgressBarSegment";
private static final long PROGRESS_ANIMATION_DURATION_MS = 400L;
private static final long OVER_SWEEP_ANIMATION_DELAY_MS = 200L;
private static final long OVER_SWEEP_ANIMATION_DURATION_MS = 200L;
private static final float STROKE_WIDTH_DP = 12f;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@NonNull private final Rect mBounds;
@NonNull private final Runnable mInvalidateRunnable;
private final float mStartAngle;
private final float mSweepAngle;
private final float mMaxOverSweepAngle;
private final float mStrokeWidthPx;
@NonNull private final Paint mBackgroundPaint;
@NonNull private final Paint mProgressPaint;
private boolean mIsFilledOrFilling = false;
private float mProgress = 0f;
@Nullable private ValueAnimator mProgressAnimator;
@NonNull private final ValueAnimator.AnimatorUpdateListener mProgressUpdateListener;
private float mOverSweepAngle = 0f;
@Nullable private ValueAnimator mOverSweepAnimator;
@Nullable private ValueAnimator mOverSweepReverseAnimator;
@NonNull private final ValueAnimator.AnimatorUpdateListener mOverSweepUpdateListener;
@NonNull private final Runnable mOverSweepAnimationRunnable;
public UdfpsEnrollProgressBarSegment(@NonNull Context context, @NonNull Rect bounds,
float startAngle, float sweepAngle, float maxOverSweepAngle,
@NonNull Runnable invalidateRunnable) {
mBounds = bounds;
mInvalidateRunnable = invalidateRunnable;
mStartAngle = startAngle;
mSweepAngle = sweepAngle;
mMaxOverSweepAngle = maxOverSweepAngle;
mStrokeWidthPx = Utils.dpToPixels(context, STROKE_WIDTH_DP);
mBackgroundPaint = new Paint();
mBackgroundPaint.setStrokeWidth(mStrokeWidthPx);
mBackgroundPaint.setColor(context.getColor(R.color.white_disabled));
mBackgroundPaint.setAntiAlias(true);
mBackgroundPaint.setStyle(Paint.Style.STROKE);
mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
// Background paint color + alpha
final int[] attrs = new int[] {android.R.attr.colorControlNormal};
final TypedArray ta = context.obtainStyledAttributes(attrs);
@ColorInt final int tintColor = ta.getColor(0, mBackgroundPaint.getColor());
mBackgroundPaint.setColor(tintColor);
ta.recycle();
TypedValue alpha = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, alpha, true);
mBackgroundPaint.setAlpha((int) (alpha.getFloat() * 255f));
// Progress should not be color extracted
mProgressPaint = new Paint();
mProgressPaint.setStrokeWidth(mStrokeWidthPx);
mProgressPaint.setColor(context.getColor(R.color.udfps_enroll_progress));
mProgressPaint.setAntiAlias(true);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
mProgressUpdateListener = animation -> {
mProgress = (float) animation.getAnimatedValue();
mInvalidateRunnable.run();
};
mOverSweepUpdateListener = animation -> {
mOverSweepAngle = (float) animation.getAnimatedValue();
mInvalidateRunnable.run();
};
mOverSweepAnimationRunnable = () -> {
if (mOverSweepAnimator != null && mOverSweepAnimator.isRunning()) {
mOverSweepAnimator.cancel();
}
mOverSweepAnimator = ValueAnimator.ofFloat(mOverSweepAngle, mMaxOverSweepAngle);
mOverSweepAnimator.setDuration(OVER_SWEEP_ANIMATION_DURATION_MS);
mOverSweepAnimator.addUpdateListener(mOverSweepUpdateListener);
mOverSweepAnimator.start();
};
}
/**
* Draws this segment to the given canvas.
*/
public void draw(@NonNull Canvas canvas) {
Log.d(TAG, "draw: mProgress = " + mProgress);
final float halfPaddingPx = mStrokeWidthPx / 2f;
if (mProgress < 1f) {
// Draw the unfilled background color of the segment.
canvas.drawArc(
halfPaddingPx,
halfPaddingPx,
mBounds.right - halfPaddingPx,
mBounds.bottom - halfPaddingPx,
mStartAngle,
mSweepAngle,
false /* useCenter */,
mBackgroundPaint);
}
if (mProgress > 0f) {
// Draw the filled progress portion of the segment.
canvas.drawArc(
halfPaddingPx,
halfPaddingPx,
mBounds.right - halfPaddingPx,
mBounds.bottom - halfPaddingPx,
mStartAngle,
mSweepAngle * mProgress + mOverSweepAngle,
false /* useCenter */,
mProgressPaint);
}
}
/**
* @return Whether this segment is filled or in the process of being filled.
*/
public boolean isFilledOrFilling() {
return mIsFilledOrFilling;
}
/**
* Updates the fill progress of this segment, animating if necessary.
*
* @param progress The new fill progress, in the range [0, 1].
*/
public void updateProgress(float progress) {
updateProgress(progress, PROGRESS_ANIMATION_DURATION_MS);
}
private void updateProgress(float progress, long animationDurationMs) {
Log.d(TAG, "updateProgress: progress = " + progress
+ ", duration = " + animationDurationMs);
if (mProgress == progress) {
Log.d(TAG, "updateProgress skipped: progress == mProgress");
return;
}
mIsFilledOrFilling = progress >= 1f;
if (mProgressAnimator != null && mProgressAnimator.isRunning()) {
mProgressAnimator.cancel();
}
mProgressAnimator = ValueAnimator.ofFloat(mProgress, progress);
mProgressAnimator.setDuration(animationDurationMs);
mProgressAnimator.addUpdateListener(mProgressUpdateListener);
mProgressAnimator.start();
}
/**
* Queues and runs the completion animation for this segment.
*/
public void startCompletionAnimation() {
final boolean hasCallback = mHandler.hasCallbacks(mOverSweepAnimationRunnable);
if (hasCallback || mOverSweepAngle >= mMaxOverSweepAngle) {
Log.d(TAG, "startCompletionAnimation skipped: hasCallback = " + hasCallback
+ ", mOverSweepAngle = " + mOverSweepAngle);
return;
}
Log.d(TAG, "startCompletionAnimation: mProgress = " + mProgress
+ ", mOverSweepAngle = " + mOverSweepAngle);
// Reset sweep angle back to zero if the animation is being rolled back.
if (mOverSweepReverseAnimator != null && mOverSweepReverseAnimator.isRunning()) {
mOverSweepReverseAnimator.cancel();
mOverSweepAngle = 0f;
}
// Start filling the segment if it isn't already.
if (mProgress < 1f) {
updateProgress(1f, OVER_SWEEP_ANIMATION_DELAY_MS);
}
// Queue the animation to run after fill completes.
mHandler.postDelayed(mOverSweepAnimationRunnable, OVER_SWEEP_ANIMATION_DELAY_MS);
}
/**
* Cancels (and reverses, if necessary) a queued or running completion animation.
*/
public void cancelCompletionAnimation() {
Log.d(TAG, "cancelCompletionAnimation: mProgress = " + mProgress
+ ", mOverSweepAngle = " + mOverSweepAngle);
// Cancel the animation if it's queued or running.
mHandler.removeCallbacks(mOverSweepAnimationRunnable);
if (mOverSweepAnimator != null && mOverSweepAnimator.isRunning()) {
mOverSweepAnimator.cancel();
}
// Roll back the animation if it has at least partially run.
if (mOverSweepAngle > 0f) {
if (mOverSweepReverseAnimator != null && mOverSweepReverseAnimator.isRunning()) {
mOverSweepReverseAnimator.cancel();
}
final float completion = mOverSweepAngle / mMaxOverSweepAngle;
final long proratedDuration = (long) (OVER_SWEEP_ANIMATION_DURATION_MS * completion);
mOverSweepReverseAnimator = ValueAnimator.ofFloat(mOverSweepAngle, 0f);
mOverSweepReverseAnimator.setDuration(proratedDuration);
mOverSweepReverseAnimator.addUpdateListener(mOverSweepUpdateListener);
mOverSweepReverseAnimator.start();
}
}
}

View File

@@ -74,7 +74,7 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController<Udfp
@NonNull @NonNull
@Override @Override
public PointF getTouchTranslation() { public PointF getTouchTranslation() {
if (!mEnrollHelper.isCenterEnrollmentComplete()) { if (!mEnrollHelper.isGuidedEnrollmentStage()) {
return new PointF(0, 0); return new PointF(0, 0);
} else { } else {
return mEnrollHelper.getNextGuidedEnrollmentPoint(); return mEnrollHelper.getNextGuidedEnrollmentPoint();