Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5ca9913 authored by Aleksandr Litovchenko's avatar Aleksandr Litovchenko
Browse files

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)
parent c3cabe0a
Loading
Loading
Loading
Loading
+70 −86
Original line number Diff line number Diff line
@@ -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 (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) {
            startRadiusAnimation(mDotSize / 2, mDotSizeActivated / 2, 96,
                    mLinearOutSlowInInterpolator,
                    cellState, new Runnable() {
            animatorSetBuilder.with(createDotRadiusAnimation(cellState));
        }
        if (mDotColor != mDotActivatedColor) {
            animatorSetBuilder.with(createDotActivationColorAnimation(cellState));
        }

        animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
                        public void run() {
                            startRadiusAnimation(mDotSizeActivated / 2, mDotSize / 2, 192,
                                    mFastOutSlowInInterpolator,
                                    cellState, null);
            public void onAnimationEnd(Animator animation) {
                cellState.activationAnimator = null;
                invalidate();
            }
        });
        }
        startDotActivationColorAnimation(cellState);
        startLineEndAnimation(cellState, mInProgressX, mInProgressY,
                getCenterXForColumn(cell.column), getCenterYForRow(cell.row));
        startLineDisappearingAnimation(cellState);
        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) {
                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) {
        valueAnimator.addUpdateListener(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.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) {
    private Animator createDotRadiusAnimation(CellState state) {
        float defaultRadius = mDotSize / 2f;
        float activatedRadius = mDotSizeActivated / 2f;

        ValueAnimator.AnimatorUpdateListener animatorUpdateListener =
                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();
                };

        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;
                }
            }
        }
    }