diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index e4385a697c85a..3994fbdca2df1 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -18,6 +18,7 @@ package com.android.internal.widget;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -56,6 +57,7 @@ import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.internal.R;
+import com.android.internal.graphics.ColorUtils;
import java.util.ArrayList;
import java.util.List;
@@ -77,6 +79,9 @@ public class LockPatternView extends View {
private static final int LINE_END_ANIMATION_DURATION_MILLIS = 50;
private static final int LINE_FADE_OUT_DURATION_MILLIS = 500;
private static final int LINE_FADE_OUT_DELAY_MILLIS = 150;
+ private static final int DOT_ACTIVATION_DURATION_MILLIS = 50;
+ private static final int DOT_RADIUS_INCREASE_DURATION_MILLIS = 96;
+ private static final int DOT_RADIUS_DECREASE_DURATION_MILLIS = 192;
private final CellState[][] mCellStates;
private final int mDotSize;
@@ -156,6 +161,7 @@ public class LockPatternView extends View {
private int mErrorColor;
private int mSuccessColor;
private int mDotColor;
+ private int mDotActivatedColor;
private final Interpolator mFastOutSlowInInterpolator;
private final Interpolator mLinearOutSlowInInterpolator;
@@ -237,12 +243,11 @@ public class LockPatternView extends View {
float radius;
float translationY;
float alpha = 1f;
+ float activationAnimationProgress;
public float lineEndX = Float.MIN_VALUE;
public float lineEndY = Float.MIN_VALUE;
@Nullable
- ValueAnimator lineEndAnimator;
- @Nullable
- ValueAnimator lineDisappearingAnimator;
+ Animator activationAnimator;
}
/**
@@ -330,6 +335,7 @@ public class LockPatternView extends View {
mErrorColor = a.getColor(R.styleable.LockPatternView_errorColor, 0);
mSuccessColor = a.getColor(R.styleable.LockPatternView_successColor, 0);
mDotColor = a.getColor(R.styleable.LockPatternView_dotColor, mRegularColor);
+ mDotActivatedColor = a.getColor(R.styleable.LockPatternView_dotActivatedColor, mDotColor);
int pathColor = a.getColor(R.styleable.LockPatternView_pathColor, mRegularColor);
mPathPaint.setColor(pathColor);
@@ -798,93 +804,111 @@ public class LockPatternView extends View {
private void startCellActivatedAnimation(Cell cell) {
final CellState cellState = mCellStates[cell.row][cell.column];
- startRadiusAnimation(mDotSize/2, mDotSizeActivated/2, 96, mLinearOutSlowInInterpolator,
- cellState, new Runnable() {
- @Override
- public void run() {
- startRadiusAnimation(mDotSizeActivated/2, mDotSize/2, 192,
- mFastOutSlowInInterpolator,
- cellState, null);
- }
- });
- startLineEndAnimation(cellState, mInProgressX, mInProgressY,
- getCenterXForColumn(cell.column), getCenterYForRow(cell.row));
- startLineDisappearingAnimation(cellState);
+
+ if (cellState.activationAnimator != null) {
+ cellState.activationAnimator.cancel();
+ }
+ AnimatorSet animatorSet = new AnimatorSet();
+ AnimatorSet.Builder animatorSetBuilder = animatorSet
+ .play(createLineDisappearingAnimation())
+ .with(createLineEndAnimation(cellState, mInProgressX, mInProgressY,
+ getCenterXForColumn(cell.column), getCenterYForRow(cell.row)));
+ if (mDotSize != mDotSizeActivated) {
+ animatorSetBuilder.with(createDotRadiusAnimation(cellState));
+ }
+ if (mDotColor != mDotActivatedColor) {
+ animatorSetBuilder.with(createDotActivationColorAnimation(cellState));
+ }
+
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ cellState.activationAnimator = null;
+ invalidate();
+ }
+ });
+ cellState.activationAnimator = animatorSet;
+ animatorSet.start();
+ }
+
+ private Animator createDotActivationColorAnimation(CellState cellState) {
+ ValueAnimator.AnimatorUpdateListener updateListener =
+ valueAnimator -> {
+ cellState.activationAnimationProgress =
+ (float) valueAnimator.getAnimatedValue();
+ invalidate();
+ };
+ ValueAnimator activateAnimator = ValueAnimator.ofFloat(0f, 1f);
+ ValueAnimator deactivateAnimator = ValueAnimator.ofFloat(1f, 0f);
+ activateAnimator.addUpdateListener(updateListener);
+ deactivateAnimator.addUpdateListener(updateListener);
+ activateAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ deactivateAnimator.setInterpolator(mLinearOutSlowInInterpolator);
+
+ // Align dot animation duration with line fade out animation.
+ activateAnimator.setDuration(DOT_ACTIVATION_DURATION_MILLIS);
+ deactivateAnimator.setDuration(DOT_ACTIVATION_DURATION_MILLIS);
+ AnimatorSet set = new AnimatorSet();
+ set.play(deactivateAnimator)
+ .after(LINE_FADE_OUT_DELAY_MILLIS + LINE_FADE_OUT_DURATION_MILLIS
+ - DOT_ACTIVATION_DURATION_MILLIS * 2)
+ .after(activateAnimator);
+ return set;
}
/**
* On the last frame before cell activates the end point of in progress line is not aligned
* with dot center so we execute a short animation moving the end point to exact dot center.
*/
- private void startLineEndAnimation(final CellState state,
+ private Animator createLineEndAnimation(final CellState state,
final float startX, final float startY, final float targetX, final float targetY) {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
- valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (float) animation.getAnimatedValue();
- state.lineEndX = (1 - t) * startX + t * targetX;
- state.lineEndY = (1 - t) * startY + t * targetY;
- invalidate();
- }
- });
- valueAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- state.lineEndAnimator = null;
- }
+ valueAnimator.addUpdateListener(animation -> {
+ float t = (float) animation.getAnimatedValue();
+ state.lineEndX = (1 - t) * startX + t * targetX;
+ state.lineEndY = (1 - t) * startY + t * targetY;
+ invalidate();
});
valueAnimator.setInterpolator(mFastOutSlowInInterpolator);
valueAnimator.setDuration(LINE_END_ANIMATION_DURATION_MILLIS);
- valueAnimator.start();
- state.lineEndAnimator = valueAnimator;
+ return valueAnimator;
}
/**
* Starts animator to fade out a line segment. It does only invalidate because all the
* transitions are applied in {@code onDraw} method.
*/
- private void startLineDisappearingAnimation(final CellState state) {
+ private Animator createLineDisappearingAnimation() {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
- valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- invalidate();
- }
- });
- valueAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- state.lineDisappearingAnimator = null;
- }
- });
+ valueAnimator.addUpdateListener(animation -> invalidate());
valueAnimator.setStartDelay(LINE_FADE_OUT_DELAY_MILLIS);
valueAnimator.setDuration(LINE_FADE_OUT_DURATION_MILLIS);
- valueAnimator.start();
- state.lineDisappearingAnimator = valueAnimator;
+ return valueAnimator;
}
- private void startRadiusAnimation(float start, float end, long duration,
- Interpolator interpolator, final CellState state, final Runnable endRunnable) {
- ValueAnimator valueAnimator = ValueAnimator.ofFloat(start, end);
- valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- state.radius = (float) animation.getAnimatedValue();
- invalidate();
- }
- });
- if (endRunnable != null) {
- valueAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- endRunnable.run();
- }
- });
- }
- valueAnimator.setInterpolator(interpolator);
- valueAnimator.setDuration(duration);
- valueAnimator.start();
+ private Animator createDotRadiusAnimation(CellState state) {
+ float defaultRadius = mDotSize / 2f;
+ float activatedRadius = mDotSizeActivated / 2f;
+
+ ValueAnimator.AnimatorUpdateListener animatorUpdateListener =
+ animation -> {
+ state.radius = (float) animation.getAnimatedValue();
+ invalidate();
+ };
+
+ ValueAnimator activationAnimator = ValueAnimator.ofFloat(defaultRadius, activatedRadius);
+ activationAnimator.addUpdateListener(animatorUpdateListener);
+ activationAnimator.setInterpolator(mLinearOutSlowInInterpolator);
+ activationAnimator.setDuration(DOT_RADIUS_INCREASE_DURATION_MILLIS);
+
+ ValueAnimator deactivationAnimator = ValueAnimator.ofFloat(activatedRadius, defaultRadius);
+ deactivationAnimator.addUpdateListener(animatorUpdateListener);
+ deactivationAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ deactivationAnimator.setDuration(DOT_RADIUS_DECREASE_DURATION_MILLIS);
+
+ AnimatorSet set = new AnimatorSet();
+ set.playSequentially(activationAnimator, deactivationAnimator);
+ return set;
}
// helper method to find which cell a point maps to
@@ -1098,16 +1122,14 @@ public class LockPatternView extends View {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
CellState state = mCellStates[i][j];
- if (state.lineEndAnimator != null) {
- state.lineEndAnimator.cancel();
- state.lineEndAnimator = null;
+ if (state.activationAnimator != null) {
+ state.activationAnimator.cancel();
+ state.activationAnimator = null;
+ state.radius = mDotSize / 2f;
+ state.activationAnimationProgress = 0f;
state.lineEndX = Float.MIN_VALUE;
state.lineEndY = Float.MIN_VALUE;
}
- if (state.lineDisappearingAnimator != null) {
- state.lineDisappearingAnimator.cancel();
- state.lineDisappearingAnimator = null;
- }
}
}
}
@@ -1295,7 +1317,8 @@ public class LockPatternView extends View {
cellState.hwRadius, cellState.hwPaint);
} else {
drawCircle(canvas, (int) centerX, (int) centerY + translationY,
- cellState.radius, drawLookup[i][j], cellState.alpha);
+ cellState.radius, drawLookup[i][j], cellState.alpha,
+ cellState.activationAnimationProgress);
}
}
}
@@ -1395,8 +1418,14 @@ public class LockPatternView extends View {
* @param partOfPattern Whether this circle is part of the pattern.
*/
private void drawCircle(Canvas canvas, float centerX, float centerY, float radius,
- boolean partOfPattern, float alpha) {
- mPaint.setColor(getDotColor());
+ boolean partOfPattern, float alpha, float activationAnimationProgress) {
+ if (mFadePattern && !mInStealthMode) {
+ int resultColor = ColorUtils.blendARGB(mDotColor, mDotActivatedColor,
+ /* ratio= */ activationAnimationProgress);
+ mPaint.setColor(resultColor);
+ } else {
+ mPaint.setColor(getDotColor());
+ }
mPaint.setAlpha((int) (alpha * 255));
canvas.drawCircle(centerX, centerY, radius, mPaint);
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a5f505176d5d1..a56ed147876a2 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8658,6 +8658,8 @@
+
+