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

Commit 4b44b7ad authored by Selim Cinek's avatar Selim Cinek Committed by Android Git Automerger
Browse files

am bdb5b5d1: Merge "Implemented the appear animation for the pattern...

am bdb5b5d1: Merge "Implemented the appear animation for the pattern security." into lmp-preview-dev

* commit 'bdb5b5d1cc1b248003b0e86a2fe2793230fe5bf3':
  Implemented the appear animation for the pattern security.
parents b2367cbf 79525b2f
Loading
Loading
Loading
Loading
+34 −8
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ public class LockPatternView extends View {
    private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h)

    private static final boolean PROFILE_DRAWING = false;
    private final CellState[][] mCellStates;
    private boolean mDrawingProfilingStarted = false;

    private Paint mPaint = new Paint();
@@ -187,6 +188,12 @@ public class LockPatternView extends View {
        }
    }

    public static class CellState {
        public float scale = 1.0f;
        public float translateY = 0.0f;
        public float alpha = 1.0f;
     }

    /**
     * How to display the current pattern.
     */
@@ -296,6 +303,18 @@ public class LockPatternView extends View {
            mBitmapHeight = Math.max(mBitmapHeight, bitmap.getHeight());
        }

        mPaint.setFilterBitmap(true);

        mCellStates = new CellState[3][3];
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                mCellStates[i][j] = new CellState();
            }
        }
    }

    public CellState[][] getCellStates() {
        return mCellStates;
    }

    private Bitmap getBitmapFor(int resId) {
@@ -873,18 +892,22 @@ public class LockPatternView extends View {
            //float centerY = mPaddingTop + i * mSquareHeight + (mSquareHeight / 2);
            for (int j = 0; j < 3; j++) {
                float leftX = paddingLeft + j * squareWidth;
                drawCircle(canvas, (int) leftX, (int) topY, drawLookup[i][j]);
                float scale = mCellStates[i][j].scale;
                mPaint.setAlpha((int) (mCellStates[i][j].alpha * 255));
                float translationY = mCellStates[i][j].translateY;
                drawCircle(canvas, (int) leftX, (int) topY + translationY, scale, drawLookup[i][j]);
            }
        }

        // Reset the alpha to draw normally
        mPaint.setAlpha(255);

        // TODO: the path should be created and cached every time we hit-detect a cell
        // only the last segment of the path should be computed here
        // draw the path of the pattern (unless we are in stealth mode)
        final boolean drawPath = !mInStealthMode;

        // draw the arrows associated with the path (unless we are in stealth mode)
        boolean oldFlag = (mPaint.getFlags() & Paint.FILTER_BITMAP_FLAG) != 0;
        mPaint.setFilterBitmap(true); // draw with higher quality since we render with transforms
        if (drawPath) {
            for (int i = 0; i < count - 1; i++) {
                Cell cell = pattern.get(i);
@@ -898,7 +921,8 @@ public class LockPatternView extends View {
                }

                float leftX = paddingLeft + cell.column * squareWidth;
                float topY = paddingTop + cell.row * squareHeight;
                float topY = paddingTop + cell.row * squareHeight
                        + mCellStates[cell.row][cell.column].translateY;

                drawArrow(canvas, leftX, topY, cell, next);
            }
@@ -919,6 +943,9 @@ public class LockPatternView extends View {

                float centerX = getCenterXForColumn(cell.column);
                float centerY = getCenterYForRow(cell.row);

                // Respect translation in animation
                centerY += mCellStates[cell.row][cell.column].translateY;
                if (i == 0) {
                    currentPath.moveTo(centerX, centerY);
                } else {
@@ -933,8 +960,6 @@ public class LockPatternView extends View {
            }
            canvas.drawPath(currentPath, mPathPaint);
        }

        mPaint.setFilterBitmap(oldFlag); // restore default flag
    }

    private void drawArrow(Canvas canvas, float leftX, float topY, Cell start, Cell end) {
@@ -979,7 +1004,8 @@ public class LockPatternView extends View {
     * @param topY
     * @param partOfPattern Whether this circle is part of the pattern.
     */
    private void drawCircle(Canvas canvas, int leftX, int topY, boolean partOfPattern) {
    private void drawCircle(Canvas canvas, float leftX, float topY, float scale,
            boolean partOfPattern) {
        Bitmap outerCircle;
        Bitmap innerCircle;

@@ -1019,7 +1045,7 @@ public class LockPatternView extends View {

        mCircleMatrix.setTranslate(leftX + offsetX, topY + offsetY);
        mCircleMatrix.preTranslate(mBitmapWidth/2, mBitmapHeight/2);
        mCircleMatrix.preScale(sx, sy);
        mCircleMatrix.preScale(sx * scale, sy * scale);
        mCircleMatrix.preTranslate(-mBitmapWidth/2, -mBitmapHeight/2);

        canvas.drawBitmap(outerCircle, mCircleMatrix, mPaint);
+1 −1
Original line number Diff line number Diff line
@@ -162,5 +162,5 @@
    <dimen name="big_font_size">120dp</dimen>

    <!-- The y translation to apply at the start in appear animations. -->
    <dimen name="appear_y_translation_start">24dp</dimen>
    <dimen name="appear_y_translation_start">32dp</dimen>
</resources>
+29 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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.keyguard;

import android.animation.Animator;
import android.view.animation.Interpolator;

/**
 * An interface which can create animations when starting an appear animation with
 * {@link com.android.keyguard.AppearAnimationUtils}
 */
public interface AppearAnimationCreator<T> {
     void createAnimation(T animatedObject, long delay, long duration,
            float startTranslationY, Interpolator interpolator, Runnable finishListener);
}
+83 −43
Original line number Diff line number Diff line
@@ -16,84 +16,124 @@

package com.android.keyguard;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
import android.content.Context;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;

/**
 * A class to make nice appear transitions for views in a tabular layout.
 */
public class AppearAnimationUtils {
public class AppearAnimationUtils implements AppearAnimationCreator<View> {

    public static final long APPEAR_DURATION = 220;

    private final Interpolator mLinearOutSlowIn;
    private final float mStartTranslation;
    private final AppearAnimationProperties mProperties = new AppearAnimationProperties();
    private final float mDelayScale;

    public AppearAnimationUtils(Context ctx) {
        this(ctx, 1.0f, 1.0f);
    }

    public AppearAnimationUtils(Context ctx, float delayScaleFactor,
            float translationScaleFactor) {
        mLinearOutSlowIn = AnimationUtils.loadInterpolator(
                ctx, android.R.interpolator.linear_out_slow_in);
        mStartTranslation =
                ctx.getResources().getDimensionPixelOffset(R.dimen.appear_y_translation_start);
        mStartTranslation = ctx.getResources().getDimensionPixelOffset(
                R.dimen.appear_y_translation_start) * translationScaleFactor;
        mDelayScale = delayScaleFactor;
    }

    public void startAppearAnimation(View[][] views, final Runnable finishListener) {
    public void startAppearAnimation(View[][] objects, final Runnable finishListener) {
        startAppearAnimation(objects, finishListener, this);
    }

    public <T> void startAppearAnimation(T[][] objects, final Runnable finishListener,
            AppearAnimationCreator<T> creator) {
        AppearAnimationProperties properties = getDelays(objects);
        startAnimations(properties, objects, finishListener, creator);
    }

    private <T> void startAnimations(AppearAnimationProperties properties, T[][] objects,
            final Runnable finishListener, AppearAnimationCreator creator) {;
        if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
            finishListener.run();
            return;
        }
        for (int row = 0; row < properties.delays.length; row++) {
            long[] columns = properties.delays[row];
            for (int col = 0; col < columns.length; col++) {
                long delay = columns[col];
                Runnable endRunnable = null;
                if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == col) {
                    endRunnable = finishListener;
                }
                creator.createAnimation(objects[row][col], delay, APPEAR_DURATION,
                        mStartTranslation, mLinearOutSlowIn, endRunnable);
            }
        }

    }

    private <T> AppearAnimationProperties getDelays(T[][] items) {
        long maxDelay = 0;
        ViewPropertyAnimator maxDelayAnimator = null;
        for (int row = 0; row < views.length; row++) {
            View[] columns = views[row];
        mProperties.maxDelayColIndex = -1;
        mProperties.maxDelayRowIndex = -1;
        mProperties.delays = new long[items.length][];
        for (int row = 0; row < items.length; row++) {
            T[] columns = items[row];
            mProperties.delays[row] = new long[columns.length];
            for (int col = 0; col < columns.length; col++) {
                long delay = calculateDelay(row, col);
                ViewPropertyAnimator animator = startAppearAnimation(columns[col], delay);
                if (animator != null && delay > maxDelay) {
                mProperties.delays[row][col] = delay;
                if (items[row][col] != null && delay > maxDelay) {
                    maxDelay = delay;
                    maxDelayAnimator = animator;
                    mProperties.maxDelayColIndex = col;
                    mProperties.maxDelayRowIndex = row;
                }
            }
        }
        if (maxDelayAnimator != null) {
            maxDelayAnimator.setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    finishListener.run();
        return mProperties;
    }
            });
        } else {
            finishListener.run();

    private long calculateDelay(int row, int col) {
        return (long) ((row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20) * mDelayScale);
    }

    public Interpolator getInterpolator() {
        return mLinearOutSlowIn;
    }

    public float getStartTranslation() {
        return mStartTranslation;
    }

    private ViewPropertyAnimator startAppearAnimation(View view, long delay) {
        if (view == null) return null;
    @Override
    public void createAnimation(View view, long delay, long duration, float startTranslationY,
            Interpolator interpolator, Runnable endRunnable) {
        if (view != null) {
            view.setAlpha(0f);
        view.setTranslationY(mStartTranslation);
            view.setTranslationY(startTranslationY);
            view.animate()
                    .alpha(1f)
                    .translationY(0)
                .setInterpolator(mLinearOutSlowIn)
                .setDuration(APPEAR_DURATION)
                .setStartDelay(delay)
                .setListener(null);
                    .setInterpolator(interpolator)
                    .setDuration(duration)
                    .setStartDelay(delay);
            if (view.hasOverlappingRendering()) {
                view.animate().withLayer();
            }
        return view.animate();
            if (endRunnable != null) {
                view.animate().withEndAction(endRunnable);
            }

    private long calculateDelay(int row, int col) {
        return (long) (row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20);
        }

    public TimeInterpolator getInterpolator() {
        return mLinearOutSlowIn;
    }

    public float getStartTranslation() {
        return mStartTranslation;
    public class AppearAnimationProperties {
        public long[][] delays;
        public int maxDelayRowIndex;
        public int maxDelayColIndex;
    }
}
+80 −4
Original line number Diff line number Diff line
@@ -21,6 +21,9 @@ import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -28,10 +31,13 @@ import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.Button;
import android.widget.LinearLayout;

@@ -41,7 +47,8 @@ import com.android.internal.widget.LockPatternView;
import java.io.IOException;
import java.util.List;

public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView {
public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView,
        AppearAnimationCreator<LockPatternView.CellState> {

    private static final String TAG = "SecurityPatternView";
    private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -59,6 +66,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
    private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;

    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
    private final AppearAnimationUtils mAppearAnimationUtils;

    private CountDownTimer mCountdownTimer = null;
    private LockPatternUtils mLockPatternUtils;
@@ -87,6 +95,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
    private SecurityMessageDisplay mSecurityMessageDisplay;
    private View mEcaView;
    private Drawable mBouncerFrame;
    private ViewGroup mKeyguardBouncerFrame;
    private KeyguardMessageArea mHelpMessage;

    enum FooterMode {
        Normal,
@@ -101,6 +111,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
    public KeyguardPatternView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
        mAppearAnimationUtils = new AppearAnimationUtils(context, 1.5f /* delayScale */,
                2.0f /* transitionScale */);
    }

    public void setKeyguardCallback(KeyguardSecurityCallback callback) {
@@ -148,6 +160,9 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
        if (bouncerFrameView != null) {
            mBouncerFrame = bouncerFrameView.getBackground();
        }

        mKeyguardBouncerFrame = (ViewGroup) findViewById(R.id.keyguard_bouncer_frame);
        mHelpMessage = (KeyguardMessageArea) findViewById(R.id.keyguard_message_area);
    }

    private void updateFooter(FooterMode mode) {
@@ -403,8 +418,69 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit

    @Override
    public void startAppearAnimation() {
        // TODO: Fancy animation.
        setAlpha(0);
        animate().alpha(1).withLayer().setDuration(200);
        enableClipping(false);
        mAppearAnimationUtils.startAppearAnimation(
                mLockPatternView.getCellStates(),
                new Runnable() {
                    @Override
                    public void run() {
                        enableClipping(true);
                    }
                },
                this);
        if (!TextUtils.isEmpty(mHelpMessage.getText())) {
            mAppearAnimationUtils.createAnimation(mHelpMessage, 0,
                    AppearAnimationUtils.APPEAR_DURATION,
                    mAppearAnimationUtils.getStartTranslation(),
                    mAppearAnimationUtils.getInterpolator(),
                    null /* finishRunnable */);
        }
    }

    private void enableClipping(boolean enable) {
        setClipChildren(enable);
        mKeyguardBouncerFrame.setClipToPadding(enable);
        mKeyguardBouncerFrame.setClipChildren(enable);
    }

    @Override
    public void createAnimation(final LockPatternView.CellState animatedCell, long delay,
            long duration, float startTranslationY, Interpolator interpolator,
            final Runnable finishListener) {
        animatedCell.scale = 0.0f;
        animatedCell.translateY = startTranslationY;
        ValueAnimator animator = ValueAnimator.ofFloat(startTranslationY, 0.0f);
        animator.setInterpolator(interpolator);
        animator.setDuration(duration);
        animator.setStartDelay(delay);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float animatedFraction = animation.getAnimatedFraction();
                animatedCell.scale = animatedFraction;
                animatedCell.translateY = (float) animation.getAnimatedValue();
                mLockPatternView.invalidate();
            }
        });
        if (finishListener != null) {
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    finishListener.run();
                }
            });

            // Also animate the Emergency call
            mAppearAnimationUtils.createAnimation(mEcaView, delay, duration, startTranslationY,
            interpolator, null);

            // And the forgot pattern button
            if (mForgotPatternButton.getVisibility() == View.VISIBLE) {
                mAppearAnimationUtils.createAnimation(mForgotPatternButton, delay, duration,
                        startTranslationY, interpolator, null);
            }
        }
        animator.start();
        mLockPatternView.invalidate();
    }
}