LockPatternView: Combine all cell activation animations.

Combine dot radius, dot color, line disappearing, line end animations together. It helps to reduce inconsistent states when one animation is cancelled and another is still running.

Bug: 196067470
Change-Id: Ieac0bb3f329ac94924e3c5dd5a4a4e119404a56c
(cherry picked from commit f5436912207f512493270ee5a1824b89f792aea4)
This commit is contained in:
Aleksandr Litovchenko
2021-08-17 20:26:35 +01:00
parent c3cabe0af9
commit f5ca99130a

View File

@@ -80,6 +80,8 @@ public class LockPatternView extends View {
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;
@@ -245,9 +247,7 @@ public class LockPatternView extends View {
public float lineEndX = Float.MIN_VALUE;
public float lineEndY = Float.MIN_VALUE;
@Nullable
ValueAnimator lineEndAnimator;
@Nullable
ValueAnimator lineDisappearingAnimator;
Animator activationAnimator;
}
/**
@@ -804,33 +804,39 @@ public class LockPatternView extends View {
private void startCellActivatedAnimation(Cell cell) {
final CellState cellState = mCellStates[cell.row][cell.column];
if (mDotSize != mDotSizeActivated) {
startRadiusAnimation(mDotSize / 2, mDotSizeActivated / 2, 96,
mLinearOutSlowInInterpolator,
cellState, new Runnable() {
@Override
public void run() {
startRadiusAnimation(mDotSizeActivated / 2, mDotSize / 2, 192,
mFastOutSlowInInterpolator,
cellState, null);
}
});
if (cellState.activationAnimator != null) {
cellState.activationAnimator.cancel();
}
startDotActivationColorAnimation(cellState);
startLineEndAnimation(cellState, mInProgressX, mInProgressY,
getCenterXForColumn(cell.column), getCenterYForRow(cell.row));
startLineDisappearingAnimation(cellState);
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 void startDotActivationColorAnimation(CellState cellState) {
private Animator createDotActivationColorAnimation(CellState cellState) {
ValueAnimator.AnimatorUpdateListener updateListener =
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
cellState.activationAnimationProgress =
(float) valueAnimator.getAnimatedValue();
invalidate();
}
valueAnimator -> {
cellState.activationAnimationProgress =
(float) valueAnimator.getAnimatedValue();
invalidate();
};
ValueAnimator activateAnimator = ValueAnimator.ofFloat(0f, 1f);
ValueAnimator deactivateAnimator = ValueAnimator.ofFloat(1f, 0f);
@@ -847,82 +853,62 @@ public class LockPatternView extends View {
.after(LINE_FADE_OUT_DELAY_MILLIS + LINE_FADE_OUT_DURATION_MILLIS
- DOT_ACTIVATION_DURATION_MILLIS * 2)
.after(activateAnimator);
set.start();
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
@@ -1136,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;
}
}
}
}