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 @@ + +