14e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek/*
24e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * Copyright (C) 2014 The Android Open Source Project
34e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek *
44e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * Licensed under the Apache License, Version 2.0 (the "License");
54e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * you may not use this file except in compliance with the License.
64e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * You may obtain a copy of the License at
74e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek *
84e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek *      http://www.apache.org/licenses/LICENSE-2.0
94e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek *
104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * Unless required by applicable law or agreed to in writing, software
114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * distributed under the License is distributed on an "AS IS" BASIS,
124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * See the License for the specific language governing permissions and
144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * limitations under the License
154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */
164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekpackage com.android.keyguard;
184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.animation.Animator;
204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.animation.AnimatorListenerAdapter;
214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.animation.AnimatorSet;
224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.animation.ValueAnimator;
234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.content.Context;
244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.content.res.TypedArray;
254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.graphics.Canvas;
264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.graphics.Paint;
274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.graphics.Rect;
284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.graphics.Typeface;
294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.os.PowerManager;
304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.os.SystemClock;
31a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roosimport android.os.UserHandle;
324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.provider.Settings;
33a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roosimport android.text.InputType;
34a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roosimport android.text.TextUtils;
354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.util.AttributeSet;
3690427507882de8ddbd178c6466896deb4969f5bcEvan Roskyimport android.view.Gravity;
374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.view.View;
38a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roosimport android.view.accessibility.AccessibilityEvent;
39a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roosimport android.view.accessibility.AccessibilityManager;
40a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roosimport android.view.accessibility.AccessibilityNodeInfo;
414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.view.animation.AnimationUtils;
424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.view.animation.Interpolator;
434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport java.util.ArrayList;
454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport java.util.Stack;
464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek/**
484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * A View similar to a textView which contains password text and can animate when the text is
494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * changed
504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */
514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekpublic class PasswordTextView extends View {
524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private static final float DOT_OVERSHOOT_FACTOR = 1.5f;
544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private static final long DOT_APPEAR_DURATION_OVERSHOOT = 320;
554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private static final long APPEAR_DURATION = 160;
564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private static final long DISAPPEAR_DURATION = 160;
574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private static final long RESET_DELAY_PER_ELEMENT = 40;
584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private static final long RESET_MAX_DELAY = 200;
594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    /**
614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek     * The overlap between the text disappearing and the dot appearing animation
624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek     */
634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private static final long DOT_APPEAR_TEXT_DISAPPEAR_OVERLAP_DURATION = 130;
644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    /**
664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek     * The duration the text needs to stay there at least before it can morph into a dot
674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek     */
684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private static final long TEXT_REST_DURATION_AFTER_APPEAR = 100;
694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    /**
714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek     * The duration the text should be visible, starting with the appear animation
724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek     */
734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private static final long TEXT_VISIBILITY_DURATION = 1300;
744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    /**
764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek     * The position in time from [0,1] where the overshoot should be finished and the settle back
774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek     * animation of the dot should start
784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek     */
794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private static final float OVERSHOOT_TIME_POSITION = 0.5f;
804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    /**
824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek     * The raw text size, will be multiplied by the scaled density when drawn
834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek     */
844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private final int mTextHeightRaw;
8590427507882de8ddbd178c6466896deb4969f5bcEvan Rosky    private final int mGravity;
864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private ArrayList<CharState> mTextChars = new ArrayList<>();
874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private String mText = "";
884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private Stack<CharState> mCharPool = new Stack<>();
894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private int mDotSize;
904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private PowerManager mPM;
914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private int mCharPadding;
924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private final Paint mDrawPaint = new Paint();
934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private Interpolator mAppearInterpolator;
944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private Interpolator mDisappearInterpolator;
954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private Interpolator mFastOutSlowInInterpolator;
964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private boolean mShowPassword;
9709eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia    private UserActivityListener mUserActivityListener;
9809eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia
9909eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia    public interface UserActivityListener {
10009eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia        void onUserActivity();
10109eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia    }
1024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
1034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    public PasswordTextView(Context context) {
1044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        this(context, null);
1054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
1064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
1074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    public PasswordTextView(Context context, AttributeSet attrs) {
1084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        this(context, attrs, 0);
1094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
1104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
1114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    public PasswordTextView(Context context, AttributeSet attrs, int defStyleAttr) {
1124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        this(context, attrs, defStyleAttr, 0);
1134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
1144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
1154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    public PasswordTextView(Context context, AttributeSet attrs, int defStyleAttr,
1164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            int defStyleRes) {
1174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        super(context, attrs, defStyleAttr, defStyleRes);
1184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        setFocusableInTouchMode(true);
1194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        setFocusable(true);
1204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PasswordTextView);
1214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        try {
1224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            mTextHeightRaw = a.getInt(R.styleable.PasswordTextView_scaledTextSize, 0);
12390427507882de8ddbd178c6466896deb4969f5bcEvan Rosky            mGravity = a.getInt(R.styleable.PasswordTextView_android_gravity, Gravity.CENTER);
12490427507882de8ddbd178c6466896deb4969f5bcEvan Rosky            mDotSize = a.getDimensionPixelSize(R.styleable.PasswordTextView_dotSize,
12590427507882de8ddbd178c6466896deb4969f5bcEvan Rosky                    getContext().getResources().getDimensionPixelSize(R.dimen.password_dot_size));
12690427507882de8ddbd178c6466896deb4969f5bcEvan Rosky            mCharPadding = a.getDimensionPixelSize(R.styleable.PasswordTextView_charPadding,
12790427507882de8ddbd178c6466896deb4969f5bcEvan Rosky                    getContext().getResources().getDimensionPixelSize(
12890427507882de8ddbd178c6466896deb4969f5bcEvan Rosky                            R.dimen.password_char_padding));
1294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        } finally {
1304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            a.recycle();
1314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
1324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mDrawPaint.setFlags(Paint.SUBPIXEL_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
1334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mDrawPaint.setTextAlign(Paint.Align.CENTER);
1344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mDrawPaint.setColor(0xffffffff);
1354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mDrawPaint.setTypeface(Typeface.create("sans-serif-light", 0));
1364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mShowPassword = Settings.System.getInt(mContext.getContentResolver(),
1374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                Settings.System.TEXT_SHOW_PASSWORD, 1) == 1;
1384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
1394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                android.R.interpolator.linear_out_slow_in);
1404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
1414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                android.R.interpolator.fast_out_linear_in);
1424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
1434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                android.R.interpolator.fast_out_slow_in);
1444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
1454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
1464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
1474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    @Override
1484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    protected void onDraw(Canvas canvas) {
1494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        float totalDrawingWidth = getDrawingWidth();
15090427507882de8ddbd178c6466896deb4969f5bcEvan Rosky        float currentDrawPosition;
15122d1201c39d734273ed4a356de0df0c082623479Evan Rosky        if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT) {
15222d1201c39d734273ed4a356de0df0c082623479Evan Rosky            if ((mGravity & Gravity.RELATIVE_LAYOUT_DIRECTION) != 0
15322d1201c39d734273ed4a356de0df0c082623479Evan Rosky                    && getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
15490427507882de8ddbd178c6466896deb4969f5bcEvan Rosky                currentDrawPosition = getWidth() - getPaddingRight() - totalDrawingWidth;
15590427507882de8ddbd178c6466896deb4969f5bcEvan Rosky            } else {
15690427507882de8ddbd178c6466896deb4969f5bcEvan Rosky                currentDrawPosition = getPaddingLeft();
15790427507882de8ddbd178c6466896deb4969f5bcEvan Rosky            }
15890427507882de8ddbd178c6466896deb4969f5bcEvan Rosky        } else {
15990427507882de8ddbd178c6466896deb4969f5bcEvan Rosky            currentDrawPosition = getWidth() / 2 - totalDrawingWidth / 2;
16090427507882de8ddbd178c6466896deb4969f5bcEvan Rosky        }
1614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        int length = mTextChars.size();
1624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        Rect bounds = getCharBounds();
1634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        int charHeight = (bounds.bottom - bounds.top);
16490427507882de8ddbd178c6466896deb4969f5bcEvan Rosky        float yPosition =
16590427507882de8ddbd178c6466896deb4969f5bcEvan Rosky                (getHeight() - getPaddingBottom() - getPaddingTop()) / 2 + getPaddingTop();
16690427507882de8ddbd178c6466896deb4969f5bcEvan Rosky        canvas.clipRect(getPaddingLeft(), getPaddingTop(),
16722d1201c39d734273ed4a356de0df0c082623479Evan Rosky                getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
1684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        float charLength = bounds.right - bounds.left;
1694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        for (int i = 0; i < length; i++) {
1704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            CharState charState = mTextChars.get(i);
1714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            float charWidth = charState.draw(canvas, currentDrawPosition, charHeight, yPosition,
1724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    charLength);
1734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            currentDrawPosition += charWidth;
1744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
1754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
1764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
1776380482d081e557164393417370e0c5da22a5bdaSelim Cinek    @Override
1786380482d081e557164393417370e0c5da22a5bdaSelim Cinek    public boolean hasOverlappingRendering() {
1796380482d081e557164393417370e0c5da22a5bdaSelim Cinek        return false;
1806380482d081e557164393417370e0c5da22a5bdaSelim Cinek    }
1816380482d081e557164393417370e0c5da22a5bdaSelim Cinek
1824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private Rect getCharBounds() {
1834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        float textHeight = mTextHeightRaw * getResources().getDisplayMetrics().scaledDensity;
1844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mDrawPaint.setTextSize(textHeight);
1854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        Rect bounds = new Rect();
1864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mDrawPaint.getTextBounds("0", 0, 1, bounds);
1874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        return bounds;
1884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
1894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
1904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private float getDrawingWidth() {
1914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        int width = 0;
1924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        int length = mTextChars.size();
1934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        Rect bounds = getCharBounds();
1944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        int charLength = bounds.right - bounds.left;
1954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        for (int i = 0; i < length; i++) {
1964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            CharState charState = mTextChars.get(i);
1974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (i != 0) {
1984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                width += mCharPadding * charState.currentWidthFactor;
1994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
2004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            width += charLength * charState.currentWidthFactor;
2014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
2024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        return width;
2034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
2044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
2054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
2064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    public void append(char c) {
2074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        int visibleChars = mTextChars.size();
208a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        String textbefore = mText;
2094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mText = mText + c;
2104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        int newLength = mText.length();
2114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        CharState charState;
2124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        if (newLength > visibleChars) {
2134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            charState = obtainCharState(c);
2144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            mTextChars.add(charState);
2154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        } else {
2164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            charState = mTextChars.get(newLength - 1);
2174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            charState.whichChar = c;
2184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
2194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        charState.startAppearAnimation();
2204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
2214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        // ensure that the previous element is being swapped
2224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        if (newLength > 1) {
2234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            CharState previousState = mTextChars.get(newLength - 2);
2244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (previousState.isDotSwapPending) {
2254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                previousState.swapToDotWhenAppearFinished();
2264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
2274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
2284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        userActivity();
229a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length(), 0, 1);
2304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
2314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
23209eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia    public void setUserActivityListener(UserActivityListener userActivitiListener) {
23309eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia        mUserActivityListener = userActivitiListener;
23409eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia    }
23509eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia
2364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private void userActivity() {
2374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mPM.userActivity(SystemClock.uptimeMillis(), false);
23809eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia        if (mUserActivityListener != null) {
23909eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia            mUserActivityListener.onUserActivity();
24009eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia        }
2414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
2424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
2434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    public void deleteLastChar() {
2444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        int length = mText.length();
245a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        String textbefore = mText;
2464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        if (length > 0) {
2474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            mText = mText.substring(0, length - 1);
2484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            CharState charState = mTextChars.get(length - 1);
2494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            charState.startRemoveAnimation(0, 0);
2504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
2514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        userActivity();
252a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length() - 1, 1, 0);
2534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
2544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
2554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    public String getText() {
2564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        return mText;
2574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
2584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
2594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private CharState obtainCharState(char c) {
2604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        CharState charState;
2614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        if(mCharPool.isEmpty()) {
2624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            charState = new CharState();
2634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        } else {
2644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            charState = mCharPool.pop();
2654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            charState.reset();
2664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
2674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        charState.whichChar = c;
2684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        return charState;
2694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
2704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
2714db942c21bac097a4c87b479d07d47cc3d7cc002Jim Miller    public void reset(boolean animated, boolean announce) {
272a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        String textbefore = mText;
2734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        mText = "";
2744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        int length = mTextChars.size();
2754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        int middleIndex = (length - 1) / 2;
2764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        long delayPerElement = RESET_DELAY_PER_ELEMENT;
2774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        for (int i = 0; i < length; i++) {
2784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            CharState charState = mTextChars.get(i);
2794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (animated) {
2804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                int delayIndex;
2814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                if (i <= middleIndex) {
2824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    delayIndex = i * 2;
2834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                } else {
2844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    int distToMiddle = i - middleIndex;
2854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    delayIndex = (length - 1) - (distToMiddle - 1) * 2;
2864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                }
2874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                long startDelay = delayIndex * delayPerElement;
2884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                startDelay = Math.min(startDelay, RESET_MAX_DELAY);
2894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                long maxDelay = delayPerElement * (length - 1);
2904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                maxDelay = Math.min(maxDelay, RESET_MAX_DELAY) + DISAPPEAR_DURATION;
2914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                charState.startRemoveAnimation(startDelay, maxDelay);
2924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                charState.removeDotSwapCallbacks();
2934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            } else {
2944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                mCharPool.push(charState);
2954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
2964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
2974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        if (!animated) {
2984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            mTextChars.clear();
2994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
3004db942c21bac097a4c87b479d07d47cc3d7cc002Jim Miller        if (announce) {
3014db942c21bac097a4c87b479d07d47cc3d7cc002Jim Miller            sendAccessibilityEventTypeViewTextChanged(textbefore, 0, textbefore.length(), 0);
3024db942c21bac097a4c87b479d07d47cc3d7cc002Jim Miller        }
303a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    }
304a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos
305a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    void sendAccessibilityEventTypeViewTextChanged(String beforeText, int fromIndex,
306a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos                                                   int removedCount, int addedCount) {
307a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        if (AccessibilityManager.getInstance(mContext).isEnabled() &&
308a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos                (isFocused() || isSelected() && isShown())) {
309a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos            if (!shouldSpeakPasswordsForAccessibility()) {
310a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos                beforeText = null;
311a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos            }
312a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos            AccessibilityEvent event =
313a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos                    AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
314a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos            event.setFromIndex(fromIndex);
315a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos            event.setRemovedCount(removedCount);
316a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos            event.setAddedCount(addedCount);
317a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos            event.setBeforeText(beforeText);
318a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos            event.setPassword(true);
319a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos            sendAccessibilityEventUnchecked(event);
320a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        }
321a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    }
322a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos
323a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    @Override
324a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
325a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        super.onInitializeAccessibilityEvent(event);
326a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos
327a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        event.setClassName(PasswordTextView.class.getName());
328a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        event.setPassword(true);
329a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    }
330a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos
331a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    @Override
332a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
333a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        super.onPopulateAccessibilityEvent(event);
334a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos
335a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        if (shouldSpeakPasswordsForAccessibility()) {
336a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos            final CharSequence text = mText;
337a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos            if (!TextUtils.isEmpty(text)) {
338a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos                event.getText().add(text);
339a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos            }
340a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        }
341a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    }
342a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos
343a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    @Override
344a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
345a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        super.onInitializeAccessibilityNodeInfo(info);
346a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos
347a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        info.setClassName(PasswordTextView.class.getName());
348a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        info.setPassword(true);
349a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos
350a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        if (shouldSpeakPasswordsForAccessibility()) {
351a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos            info.setText(mText);
352a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        }
353a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos
354a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        info.setEditable(true);
355a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos
356a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        info.setInputType(InputType.TYPE_NUMBER_VARIATION_PASSWORD);
357a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    }
358a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos
359a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    /**
360a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos     * @return true if the user has explicitly allowed accessibility services
361a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos     * to speak passwords.
362a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos     */
363a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos    private boolean shouldSpeakPasswordsForAccessibility() {
364a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos        return (Settings.Secure.getIntForUser(mContext.getContentResolver(),
365a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos                Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0,
366a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos                UserHandle.USER_CURRENT_OR_SELF) == 1);
3674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
3684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
3694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    private class CharState {
3704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        char whichChar;
3714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        ValueAnimator textAnimator;
3724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        boolean textAnimationIsGrowing;
3734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        Animator dotAnimator;
3744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        boolean dotAnimationIsGrowing;
3754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        ValueAnimator widthAnimator;
3764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        boolean widthAnimationIsGrowing;
3774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        float currentTextSizeFactor;
3784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        float currentDotSizeFactor;
3794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        float currentWidthFactor;
3804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        boolean isDotSwapPending;
3814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        float currentTextTranslationY = 1.0f;
3824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        ValueAnimator textTranslateAnimator;
3834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
3844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        Animator.AnimatorListener removeEndListener = new AnimatorListenerAdapter() {
3854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            private boolean mCancelled;
3864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            @Override
3874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            public void onAnimationCancel(Animator animation) {
3884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                mCancelled = true;
3894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
3904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
3914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            @Override
3924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            public void onAnimationEnd(Animator animation) {
3934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                if (!mCancelled) {
3944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    mTextChars.remove(CharState.this);
3954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    mCharPool.push(CharState.this);
3964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    reset();
3974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    cancelAnimator(textTranslateAnimator);
3984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    textTranslateAnimator = null;
3994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                }
4004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
4014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
4024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            @Override
4034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            public void onAnimationStart(Animator animation) {
4044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                mCancelled = false;
4054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
4064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        };
4074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
4084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        Animator.AnimatorListener dotFinishListener = new AnimatorListenerAdapter() {
4094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            @Override
4104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            public void onAnimationEnd(Animator animation) {
4114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                dotAnimator = null;
4124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
4134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        };
4144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
4154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        Animator.AnimatorListener textFinishListener = new AnimatorListenerAdapter() {
4164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            @Override
4174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            public void onAnimationEnd(Animator animation) {
4184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                textAnimator = null;
4194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
4204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        };
4214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
4224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        Animator.AnimatorListener textTranslateFinishListener = new AnimatorListenerAdapter() {
4234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            @Override
4244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            public void onAnimationEnd(Animator animation) {
4254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                textTranslateAnimator = null;
4264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
4274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        };
4284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
4294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        Animator.AnimatorListener widthFinishListener = new AnimatorListenerAdapter() {
4304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            @Override
4314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            public void onAnimationEnd(Animator animation) {
4324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                widthAnimator = null;
4334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
4344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        };
4354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
4364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private ValueAnimator.AnimatorUpdateListener dotSizeUpdater
4374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                = new ValueAnimator.AnimatorUpdateListener() {
4384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            @Override
4394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
4404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                currentDotSizeFactor = (float) animation.getAnimatedValue();
4414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                invalidate();
4424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
4434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        };
4444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
4454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private ValueAnimator.AnimatorUpdateListener textSizeUpdater
4464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                = new ValueAnimator.AnimatorUpdateListener() {
4474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            @Override
4484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
4494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                currentTextSizeFactor = (float) animation.getAnimatedValue();
4504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                invalidate();
4514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
4524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        };
4534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
4544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private ValueAnimator.AnimatorUpdateListener textTranslationUpdater
4554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                = new ValueAnimator.AnimatorUpdateListener() {
4564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            @Override
4574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
4584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                currentTextTranslationY = (float) animation.getAnimatedValue();
4594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                invalidate();
4604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
4614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        };
4624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
4634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private ValueAnimator.AnimatorUpdateListener widthUpdater
4644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                = new ValueAnimator.AnimatorUpdateListener() {
4654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            @Override
4664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
4674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                currentWidthFactor = (float) animation.getAnimatedValue();
4684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                invalidate();
4694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
4704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        };
4714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
4724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private Runnable dotSwapperRunnable = new Runnable() {
4734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            @Override
4744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            public void run() {
4754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                performSwap();
4764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                isDotSwapPending = false;
4774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
4784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        };
4794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
4804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        void reset() {
4814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            whichChar = 0;
4824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            currentTextSizeFactor = 0.0f;
4834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            currentDotSizeFactor = 0.0f;
4844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            currentWidthFactor = 0.0f;
4854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            cancelAnimator(textAnimator);
4864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator = null;
4874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            cancelAnimator(dotAnimator);
4884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            dotAnimator = null;
4894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            cancelAnimator(widthAnimator);
4904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimator = null;
4914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            currentTextTranslationY = 1.0f;
4924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            removeDotSwapCallbacks();
4934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
4944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
4954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        void startRemoveAnimation(long startDelay, long widthDelay) {
4964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            boolean dotNeedsAnimation = (currentDotSizeFactor > 0.0f && dotAnimator == null)
4974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    || (dotAnimator != null && dotAnimationIsGrowing);
4984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            boolean textNeedsAnimation = (currentTextSizeFactor > 0.0f && textAnimator == null)
4994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    || (textAnimator != null && textAnimationIsGrowing);
5004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            boolean widthNeedsAnimation = (currentWidthFactor > 0.0f && widthAnimator == null)
5014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    || (widthAnimator != null && widthAnimationIsGrowing);
5024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (dotNeedsAnimation) {
5034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                startDotDisappearAnimation(startDelay);
5044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
5054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (textNeedsAnimation) {
5064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                startTextDisappearAnimation(startDelay);
5074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
5084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (widthNeedsAnimation) {
5094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                startWidthDisappearAnimation(widthDelay);
5104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
5114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
5124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
5134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        void startAppearAnimation() {
5144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            boolean dotNeedsAnimation = !mShowPassword
5154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    && (dotAnimator == null || !dotAnimationIsGrowing);
5164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            boolean textNeedsAnimation = mShowPassword
5174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    && (textAnimator == null || !textAnimationIsGrowing);
5184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            boolean widthNeedsAnimation = (widthAnimator == null || !widthAnimationIsGrowing);
5194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (dotNeedsAnimation) {
5204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                startDotAppearAnimation(0);
5214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
5224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (textNeedsAnimation) {
5234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                startTextAppearAnimation();
5244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
5254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (widthNeedsAnimation) {
5264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                startWidthAppearAnimation();
5274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
5284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (mShowPassword) {
5294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                postDotSwap(TEXT_VISIBILITY_DURATION);
5304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
5314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
5324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
5334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        /**
5344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek         * Posts a runnable which ensures that the text will be replaced by a dot after {@link
5354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek         * com.android.keyguard.PasswordTextView#TEXT_VISIBILITY_DURATION}.
5364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek         */
5374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private void postDotSwap(long delay) {
5384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            removeDotSwapCallbacks();
5394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            postDelayed(dotSwapperRunnable, delay);
5404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            isDotSwapPending = true;
5414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
5424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
5434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private void removeDotSwapCallbacks() {
5444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            removeCallbacks(dotSwapperRunnable);
5454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            isDotSwapPending = false;
5464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
5474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
5484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        void swapToDotWhenAppearFinished() {
5494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            removeDotSwapCallbacks();
5504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (textAnimator != null) {
5514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                long remainingDuration = textAnimator.getDuration()
5524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                        - textAnimator.getCurrentPlayTime();
5534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                postDotSwap(remainingDuration + TEXT_REST_DURATION_AFTER_APPEAR);
5544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            } else {
5554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                performSwap();
5564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
5574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
5584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
5594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private void performSwap() {
5604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            startTextDisappearAnimation(0);
5614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            startDotAppearAnimation(DISAPPEAR_DURATION
5624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                    - DOT_APPEAR_TEXT_DISAPPEAR_OVERLAP_DURATION);
5634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
5644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
5654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private void startWidthDisappearAnimation(long widthDelay) {
5664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            cancelAnimator(widthAnimator);
5674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimator = ValueAnimator.ofFloat(currentWidthFactor, 0.0f);
5684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimator.addUpdateListener(widthUpdater);
5694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimator.addListener(widthFinishListener);
5704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimator.addListener(removeEndListener);
5714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimator.setDuration((long) (DISAPPEAR_DURATION * currentWidthFactor));
5724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimator.setStartDelay(widthDelay);
5734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimator.start();
5744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimationIsGrowing = false;
5754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
5764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
5774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private void startTextDisappearAnimation(long startDelay) {
5784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            cancelAnimator(textAnimator);
5794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator = ValueAnimator.ofFloat(currentTextSizeFactor, 0.0f);
5804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator.addUpdateListener(textSizeUpdater);
5814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator.addListener(textFinishListener);
5824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator.setInterpolator(mDisappearInterpolator);
5834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator.setDuration((long) (DISAPPEAR_DURATION * currentTextSizeFactor));
5844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator.setStartDelay(startDelay);
5854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator.start();
5864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimationIsGrowing = false;
5874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
5884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
5894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private void startDotDisappearAnimation(long startDelay) {
5904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            cancelAnimator(dotAnimator);
5914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            ValueAnimator animator = ValueAnimator.ofFloat(currentDotSizeFactor, 0.0f);
5924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            animator.addUpdateListener(dotSizeUpdater);
5934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            animator.addListener(dotFinishListener);
5944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            animator.setInterpolator(mDisappearInterpolator);
5954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            long duration = (long) (DISAPPEAR_DURATION * Math.min(currentDotSizeFactor, 1.0f));
5964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            animator.setDuration(duration);
5974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            animator.setStartDelay(startDelay);
5984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            animator.start();
5994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            dotAnimator = animator;
6004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            dotAnimationIsGrowing = false;
6014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
6024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
6034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private void startWidthAppearAnimation() {
6044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            cancelAnimator(widthAnimator);
6054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimator = ValueAnimator.ofFloat(currentWidthFactor, 1.0f);
6064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimator.addUpdateListener(widthUpdater);
6074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimator.addListener(widthFinishListener);
6084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimator.setDuration((long) (APPEAR_DURATION * (1f - currentWidthFactor)));
6094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimator.start();
6104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            widthAnimationIsGrowing = true;
6114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
6124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
6134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private void startTextAppearAnimation() {
6144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            cancelAnimator(textAnimator);
6154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator = ValueAnimator.ofFloat(currentTextSizeFactor, 1.0f);
6164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator.addUpdateListener(textSizeUpdater);
6174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator.addListener(textFinishListener);
6184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator.setInterpolator(mAppearInterpolator);
6194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator.setDuration((long) (APPEAR_DURATION * (1f - currentTextSizeFactor)));
6204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimator.start();
6214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            textAnimationIsGrowing = true;
6224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
6234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            // handle translation
6244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (textTranslateAnimator == null) {
6254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                textTranslateAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);
6264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                textTranslateAnimator.addUpdateListener(textTranslationUpdater);
6274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                textTranslateAnimator.addListener(textTranslateFinishListener);
6284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                textTranslateAnimator.setInterpolator(mAppearInterpolator);
6294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                textTranslateAnimator.setDuration(APPEAR_DURATION);
6304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                textTranslateAnimator.start();
6314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
6324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
6334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
6344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private void startDotAppearAnimation(long delay) {
6354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            cancelAnimator(dotAnimator);
6364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (!mShowPassword) {
6374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                // We perform an overshoot animation
6384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                ValueAnimator overShootAnimator = ValueAnimator.ofFloat(currentDotSizeFactor,
6394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                        DOT_OVERSHOOT_FACTOR);
6404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                overShootAnimator.addUpdateListener(dotSizeUpdater);
6414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                overShootAnimator.setInterpolator(mAppearInterpolator);
6424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                long overShootDuration = (long) (DOT_APPEAR_DURATION_OVERSHOOT
6434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                        * OVERSHOOT_TIME_POSITION);
6444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                overShootAnimator.setDuration(overShootDuration);
6454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                ValueAnimator settleBackAnimator = ValueAnimator.ofFloat(DOT_OVERSHOOT_FACTOR,
6464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                        1.0f);
6474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                settleBackAnimator.addUpdateListener(dotSizeUpdater);
6484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                settleBackAnimator.setDuration(DOT_APPEAR_DURATION_OVERSHOOT - overShootDuration);
6494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                settleBackAnimator.addListener(dotFinishListener);
6504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                AnimatorSet animatorSet = new AnimatorSet();
6514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                animatorSet.playSequentially(overShootAnimator, settleBackAnimator);
6524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                animatorSet.setStartDelay(delay);
6534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                animatorSet.start();
6544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                dotAnimator = animatorSet;
6554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            } else {
6564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                ValueAnimator growAnimator = ValueAnimator.ofFloat(currentDotSizeFactor, 1.0f);
6574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                growAnimator.addUpdateListener(dotSizeUpdater);
6584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                growAnimator.setDuration((long) (APPEAR_DURATION * (1.0f - currentDotSizeFactor)));
6594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                growAnimator.addListener(dotFinishListener);
6604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                growAnimator.setStartDelay(delay);
6614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                growAnimator.start();
6624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                dotAnimator = growAnimator;
6634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
6644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            dotAnimationIsGrowing = true;
6654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
6664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
6674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        private void cancelAnimator(Animator animator) {
6684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (animator != null) {
6694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                animator.cancel();
6704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
6714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
6724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek
6734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        /**
6744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek         * Draw this char to the canvas.
6754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek         *
6764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek         * @return The width this character contributes, including padding.
6774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek         */
6784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        public float draw(Canvas canvas, float currentDrawPosition, int charHeight, float yPosition,
6794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                float charLength) {
6804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            boolean textVisible = currentTextSizeFactor > 0;
6814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            boolean dotVisible = currentDotSizeFactor > 0;
6824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            float charWidth = charLength * currentWidthFactor;
6834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (textVisible) {
6844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                float currYPosition = yPosition + charHeight / 2.0f * currentTextSizeFactor
6854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                        + charHeight * currentTextTranslationY * 0.8f;
6864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                canvas.save();
6874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                float centerX = currentDrawPosition + charWidth / 2;
6884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                canvas.translate(centerX, currYPosition);
6894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                canvas.scale(currentTextSizeFactor, currentTextSizeFactor);
6904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                canvas.drawText(Character.toString(whichChar), 0, 0, mDrawPaint);
6914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                canvas.restore();
6924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
6934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            if (dotVisible) {
6944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                canvas.save();
6954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                float centerX = currentDrawPosition + charWidth / 2;
6964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                canvas.translate(centerX, yPosition);
6974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                canvas.drawCircle(0, 0, mDotSize / 2 * currentDotSizeFactor, mDrawPaint);
6984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek                canvas.restore();
6994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            }
7004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek            return charWidth + mCharPadding * currentWidthFactor;
7014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek        }
7024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek    }
7034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek}
704