blob: 4195009937c29e7e5b90faeaa593a667c11de18f [file] [log] [blame]
/*
* 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.ColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.util.TypedValue;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.R;
/**
* UDFPS enrollment progress bar.
*/
public class UdfpsEnrollProgressBarDrawable extends Drawable {
private static final String TAG = "UdfpsEnrollProgressBarDrawable";
private static final float PROGRESS_BAR_THICKNESS_DP = 12;
@NonNull private final Context mContext;
@NonNull private final UdfpsEnrollDrawable mParent;
@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,
@NonNull UdfpsEnrollDrawable parent) {
mContext = context;
mParent = parent;
mBackgroundCirclePaint = new Paint();
mBackgroundCirclePaint.setStrokeWidth(Utils.dpToPixels(context, PROGRESS_BAR_THICKNESS_DP));
mBackgroundCirclePaint.setColor(context.getColor(R.color.white_disabled));
mBackgroundCirclePaint.setAntiAlias(true);
mBackgroundCirclePaint.setStyle(Paint.Style.STROKE);
// 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) {
// Add one so that the first steps actually changes progress, but also so that the last
// step ends at 1.0
final float progress = (totalSteps - remaining + 1) / (float) (totalSteps + 1);
setEnrollmentProgress(progress);
}
private void setEnrollmentProgress(float progress) {
if (mLastStepAcquired) {
return;
}
long animationDuration = 150;
if (progress == 1.f) {
animationDuration = 400;
final ValueAnimator rotationAnimator = ValueAnimator.ofInt(0, 400);
rotationAnimator.setDuration(animationDuration);
rotationAnimator.addUpdateListener(animation -> {
Log.d(TAG, "Rotation: " + mRotation);
mRotation = (int) animation.getAnimatedValue();
mParent.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();
// Use the parent to invalidate, since it's the one that's attached as the view's
// drawable and has its callback set automatically. Invalidating via
// `this.invalidateSelf` actually does not invoke draw(), since this drawable's callback
// is not really set.
mParent.invalidateSelf();
});
mProgressAnimator.start();
}
void onLastStepAcquired() {
setEnrollmentProgress(1.f);
mLastStepAcquired = true;
}
@Override
public void draw(@NonNull Canvas canvas) {
canvas.save();
// Progress starts from the top, instead of the right
canvas.rotate(-90 + mRotation, getBounds().centerX(), getBounds().centerY());
// Progress bar "background track"
final float halfPaddingPx = Utils.dpToPixels(mContext, PROGRESS_BAR_THICKNESS_DP) / 2;
canvas.drawArc(halfPaddingPx,
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();
}
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
}
@Override
public int getOpacity() {
return 0;
}
}