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; 314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.provider.Settings; 324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.util.AttributeSet; 334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.view.View; 344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.view.animation.AnimationUtils; 354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.view.animation.Interpolator; 364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport java.util.ArrayList; 384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport java.util.Stack; 394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek/** 414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * A View similar to a textView which contains password text and can animate when the text is 424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * changed 434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekpublic class PasswordTextView extends View { 454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final float DOT_OVERSHOOT_FACTOR = 1.5f; 474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long DOT_APPEAR_DURATION_OVERSHOOT = 320; 484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long APPEAR_DURATION = 160; 494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long DISAPPEAR_DURATION = 160; 504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long RESET_DELAY_PER_ELEMENT = 40; 514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long RESET_MAX_DELAY = 200; 524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * The overlap between the text disappearing and the dot appearing animation 554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long DOT_APPEAR_TEXT_DISAPPEAR_OVERLAP_DURATION = 130; 574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * The duration the text needs to stay there at least before it can morph into a dot 604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long TEXT_REST_DURATION_AFTER_APPEAR = 100; 624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * The duration the text should be visible, starting with the appear animation 654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long TEXT_VISIBILITY_DURATION = 1300; 674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * The position in time from [0,1] where the overshoot should be finished and the settle back 704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * animation of the dot should start 714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final float OVERSHOOT_TIME_POSITION = 0.5f; 734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * The raw text size, will be multiplied by the scaled density when drawn 764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private final int mTextHeightRaw; 784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private ArrayList<CharState> mTextChars = new ArrayList<>(); 794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private String mText = ""; 804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private Stack<CharState> mCharPool = new Stack<>(); 814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private int mDotSize; 824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private PowerManager mPM; 834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private int mCharPadding; 844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private final Paint mDrawPaint = new Paint(); 854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private Interpolator mAppearInterpolator; 864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private Interpolator mDisappearInterpolator; 874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private Interpolator mFastOutSlowInInterpolator; 884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private boolean mShowPassword; 894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public PasswordTextView(Context context) { 914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek this(context, null); 924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public PasswordTextView(Context context, AttributeSet attrs) { 954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek this(context, attrs, 0); 964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public PasswordTextView(Context context, AttributeSet attrs, int defStyleAttr) { 994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek this(context, attrs, defStyleAttr, 0); 1004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public PasswordTextView(Context context, AttributeSet attrs, int defStyleAttr, 1034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int defStyleRes) { 1044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek super(context, attrs, defStyleAttr, defStyleRes); 1054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek setFocusableInTouchMode(true); 1064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek setFocusable(true); 1074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PasswordTextView); 1084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek try { 1094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mTextHeightRaw = a.getInt(R.styleable.PasswordTextView_scaledTextSize, 0); 1104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } finally { 1114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek a.recycle(); 1124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDrawPaint.setFlags(Paint.SUBPIXEL_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG); 1144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDrawPaint.setTextAlign(Paint.Align.CENTER); 1154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDrawPaint.setColor(0xffffffff); 1164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDrawPaint.setTypeface(Typeface.create("sans-serif-light", 0)); 1174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDotSize = getContext().getResources().getDimensionPixelSize(R.dimen.password_dot_size); 1184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mCharPadding = getContext().getResources().getDimensionPixelSize(R.dimen 1194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek .password_char_padding); 1204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mShowPassword = Settings.System.getInt(mContext.getContentResolver(), 1214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Settings.System.TEXT_SHOW_PASSWORD, 1) == 1; 1224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mAppearInterpolator = AnimationUtils.loadInterpolator(mContext, 1234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek android.R.interpolator.linear_out_slow_in); 1244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext, 1254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek android.R.interpolator.fast_out_linear_in); 1264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext, 1274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek android.R.interpolator.fast_out_slow_in); 1284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 1294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 1324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek protected void onDraw(Canvas canvas) { 1334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float totalDrawingWidth = getDrawingWidth(); 1344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float currentDrawPosition = getWidth() / 2 - totalDrawingWidth / 2; 1354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int length = mTextChars.size(); 1364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Rect bounds = getCharBounds(); 1374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int charHeight = (bounds.bottom - bounds.top); 1384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float yPosition = getHeight() / 2; 1394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float charLength = bounds.right - bounds.left; 1404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek for (int i = 0; i < length; i++) { 1414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState charState = mTextChars.get(i); 1424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float charWidth = charState.draw(canvas, currentDrawPosition, charHeight, yPosition, 1434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charLength); 1444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentDrawPosition += charWidth; 1454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1486380482d081e557164393417370e0c5da22a5bdaSelim Cinek @Override 1496380482d081e557164393417370e0c5da22a5bdaSelim Cinek public boolean hasOverlappingRendering() { 1506380482d081e557164393417370e0c5da22a5bdaSelim Cinek return false; 1516380482d081e557164393417370e0c5da22a5bdaSelim Cinek } 1526380482d081e557164393417370e0c5da22a5bdaSelim Cinek 1534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private Rect getCharBounds() { 1544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float textHeight = mTextHeightRaw * getResources().getDisplayMetrics().scaledDensity; 1554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDrawPaint.setTextSize(textHeight); 1564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Rect bounds = new Rect(); 1574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDrawPaint.getTextBounds("0", 0, 1, bounds); 1584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek return bounds; 1594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private float getDrawingWidth() { 1624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int width = 0; 1634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int length = mTextChars.size(); 1644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Rect bounds = getCharBounds(); 1654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int charLength = bounds.right - bounds.left; 1664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek for (int i = 0; i < length; i++) { 1674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState charState = mTextChars.get(i); 1684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (i != 0) { 1694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek width += mCharPadding * charState.currentWidthFactor; 1704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek width += charLength * charState.currentWidthFactor; 1724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek return width; 1744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void append(char c) { 1784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int visibleChars = mTextChars.size(); 1794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mText = mText + c; 1804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int newLength = mText.length(); 1814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState charState; 1824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (newLength > visibleChars) { 1834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState = obtainCharState(c); 1844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mTextChars.add(charState); 1854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } else { 1864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState = mTextChars.get(newLength - 1); 1874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.whichChar = c; 1884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.startAppearAnimation(); 1904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek // ensure that the previous element is being swapped 1924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (newLength > 1) { 1934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState previousState = mTextChars.get(newLength - 2); 1944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (previousState.isDotSwapPending) { 1954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek previousState.swapToDotWhenAppearFinished(); 1964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek userActivity(); 1994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void userActivity() { 2024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mPM.userActivity(SystemClock.uptimeMillis(), false); 2034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void deleteLastChar() { 2064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int length = mText.length(); 2074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (length > 0) { 2084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mText = mText.substring(0, length - 1); 2094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState charState = mTextChars.get(length - 1); 2104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.startRemoveAnimation(0, 0); 2114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek userActivity(); 2134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public String getText() { 2164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek return mText; 2174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private CharState obtainCharState(char c) { 2204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState charState; 2214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if(mCharPool.isEmpty()) { 2224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState = new CharState(); 2234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } else { 2244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState = mCharPool.pop(); 2254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.reset(); 2264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.whichChar = c; 2284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek return charState; 2294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void reset(boolean animated) { 2324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mText = ""; 2334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int length = mTextChars.size(); 2344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int middleIndex = (length - 1) / 2; 2354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek long delayPerElement = RESET_DELAY_PER_ELEMENT; 2364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek for (int i = 0; i < length; i++) { 2374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState charState = mTextChars.get(i); 2384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (animated) { 2394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int delayIndex; 2404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (i <= middleIndex) { 2414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek delayIndex = i * 2; 2424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } else { 2434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int distToMiddle = i - middleIndex; 2444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek delayIndex = (length - 1) - (distToMiddle - 1) * 2; 2454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek long startDelay = delayIndex * delayPerElement; 2474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startDelay = Math.min(startDelay, RESET_MAX_DELAY); 2484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek long maxDelay = delayPerElement * (length - 1); 2494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek maxDelay = Math.min(maxDelay, RESET_MAX_DELAY) + DISAPPEAR_DURATION; 2504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.startRemoveAnimation(startDelay, maxDelay); 2514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.removeDotSwapCallbacks(); 2524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } else { 2534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mCharPool.push(charState); 2544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (!animated) { 2574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mTextChars.clear(); 2584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private class CharState { 2624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek char whichChar; 2634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator textAnimator; 2644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean textAnimationIsGrowing; 2654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Animator dotAnimator; 2664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean dotAnimationIsGrowing; 2674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator widthAnimator; 2684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean widthAnimationIsGrowing; 2694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float currentTextSizeFactor; 2704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float currentDotSizeFactor; 2714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float currentWidthFactor; 2724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean isDotSwapPending; 2734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float currentTextTranslationY = 1.0f; 2744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator textTranslateAnimator; 2754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Animator.AnimatorListener removeEndListener = new AnimatorListenerAdapter() { 2774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private boolean mCancelled; 2784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 2794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationCancel(Animator animation) { 2804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mCancelled = true; 2814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 2844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationEnd(Animator animation) { 2854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (!mCancelled) { 2864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mTextChars.remove(CharState.this); 2874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mCharPool.push(CharState.this); 2884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek reset(); 2894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(textTranslateAnimator); 2904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator = null; 2914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 2954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationStart(Animator animation) { 2964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mCancelled = false; 2974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 2994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Animator.AnimatorListener dotFinishListener = new AnimatorListenerAdapter() { 3014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationEnd(Animator animation) { 3034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimator = null; 3044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 3064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Animator.AnimatorListener textFinishListener = new AnimatorListenerAdapter() { 3084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationEnd(Animator animation) { 3104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator = null; 3114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 3134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Animator.AnimatorListener textTranslateFinishListener = new AnimatorListenerAdapter() { 3154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationEnd(Animator animation) { 3174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator = null; 3184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 3204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Animator.AnimatorListener widthFinishListener = new AnimatorListenerAdapter() { 3224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationEnd(Animator animation) { 3244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator = null; 3254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 3274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private ValueAnimator.AnimatorUpdateListener dotSizeUpdater 3294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek = new ValueAnimator.AnimatorUpdateListener() { 3304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationUpdate(ValueAnimator animation) { 3324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentDotSizeFactor = (float) animation.getAnimatedValue(); 3334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek invalidate(); 3344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 3364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private ValueAnimator.AnimatorUpdateListener textSizeUpdater 3384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek = new ValueAnimator.AnimatorUpdateListener() { 3394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationUpdate(ValueAnimator animation) { 3414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentTextSizeFactor = (float) animation.getAnimatedValue(); 3424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek invalidate(); 3434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 3454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private ValueAnimator.AnimatorUpdateListener textTranslationUpdater 3474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek = new ValueAnimator.AnimatorUpdateListener() { 3484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationUpdate(ValueAnimator animation) { 3504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentTextTranslationY = (float) animation.getAnimatedValue(); 3514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek invalidate(); 3524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 3544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private ValueAnimator.AnimatorUpdateListener widthUpdater 3564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek = new ValueAnimator.AnimatorUpdateListener() { 3574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationUpdate(ValueAnimator animation) { 3594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentWidthFactor = (float) animation.getAnimatedValue(); 3604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek invalidate(); 3614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 3634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private Runnable dotSwapperRunnable = new Runnable() { 3654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void run() { 3674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek performSwap(); 3684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek isDotSwapPending = false; 3694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 3714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek void reset() { 3734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek whichChar = 0; 3744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentTextSizeFactor = 0.0f; 3754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentDotSizeFactor = 0.0f; 3764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentWidthFactor = 0.0f; 3774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(textAnimator); 3784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator = null; 3794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(dotAnimator); 3804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimator = null; 3814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(widthAnimator); 3824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator = null; 3834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentTextTranslationY = 1.0f; 3844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek removeDotSwapCallbacks(); 3854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek void startRemoveAnimation(long startDelay, long widthDelay) { 3884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean dotNeedsAnimation = (currentDotSizeFactor > 0.0f && dotAnimator == null) 3894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek || (dotAnimator != null && dotAnimationIsGrowing); 3904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean textNeedsAnimation = (currentTextSizeFactor > 0.0f && textAnimator == null) 3914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek || (textAnimator != null && textAnimationIsGrowing); 3924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean widthNeedsAnimation = (currentWidthFactor > 0.0f && widthAnimator == null) 3934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek || (widthAnimator != null && widthAnimationIsGrowing); 3944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (dotNeedsAnimation) { 3954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startDotDisappearAnimation(startDelay); 3964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (textNeedsAnimation) { 3984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startTextDisappearAnimation(startDelay); 3994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (widthNeedsAnimation) { 4014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startWidthDisappearAnimation(widthDelay); 4024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek void startAppearAnimation() { 4064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean dotNeedsAnimation = !mShowPassword 4074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek && (dotAnimator == null || !dotAnimationIsGrowing); 4084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean textNeedsAnimation = mShowPassword 4094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek && (textAnimator == null || !textAnimationIsGrowing); 4104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean widthNeedsAnimation = (widthAnimator == null || !widthAnimationIsGrowing); 4114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (dotNeedsAnimation) { 4124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startDotAppearAnimation(0); 4134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (textNeedsAnimation) { 4154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startTextAppearAnimation(); 4164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (widthNeedsAnimation) { 4184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startWidthAppearAnimation(); 4194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (mShowPassword) { 4214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek postDotSwap(TEXT_VISIBILITY_DURATION); 4224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 4264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * Posts a runnable which ensures that the text will be replaced by a dot after {@link 4274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * com.android.keyguard.PasswordTextView#TEXT_VISIBILITY_DURATION}. 4284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 4294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void postDotSwap(long delay) { 4304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek removeDotSwapCallbacks(); 4314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek postDelayed(dotSwapperRunnable, delay); 4324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek isDotSwapPending = true; 4334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void removeDotSwapCallbacks() { 4364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek removeCallbacks(dotSwapperRunnable); 4374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek isDotSwapPending = false; 4384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek void swapToDotWhenAppearFinished() { 4414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek removeDotSwapCallbacks(); 4424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (textAnimator != null) { 4434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek long remainingDuration = textAnimator.getDuration() 4444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek - textAnimator.getCurrentPlayTime(); 4454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek postDotSwap(remainingDuration + TEXT_REST_DURATION_AFTER_APPEAR); 4464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } else { 4474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek performSwap(); 4484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void performSwap() { 4524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startTextDisappearAnimation(0); 4534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startDotAppearAnimation(DISAPPEAR_DURATION 4544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek - DOT_APPEAR_TEXT_DISAPPEAR_OVERLAP_DURATION); 4554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void startWidthDisappearAnimation(long widthDelay) { 4584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(widthAnimator); 4594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator = ValueAnimator.ofFloat(currentWidthFactor, 0.0f); 4604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.addUpdateListener(widthUpdater); 4614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.addListener(widthFinishListener); 4624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.addListener(removeEndListener); 4634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.setDuration((long) (DISAPPEAR_DURATION * currentWidthFactor)); 4644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.setStartDelay(widthDelay); 4654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.start(); 4664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimationIsGrowing = false; 4674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void startTextDisappearAnimation(long startDelay) { 4704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(textAnimator); 4714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator = ValueAnimator.ofFloat(currentTextSizeFactor, 0.0f); 4724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.addUpdateListener(textSizeUpdater); 4734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.addListener(textFinishListener); 4744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.setInterpolator(mDisappearInterpolator); 4754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.setDuration((long) (DISAPPEAR_DURATION * currentTextSizeFactor)); 4764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.setStartDelay(startDelay); 4774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.start(); 4784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimationIsGrowing = false; 4794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void startDotDisappearAnimation(long startDelay) { 4824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(dotAnimator); 4834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator animator = ValueAnimator.ofFloat(currentDotSizeFactor, 0.0f); 4844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.addUpdateListener(dotSizeUpdater); 4854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.addListener(dotFinishListener); 4864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.setInterpolator(mDisappearInterpolator); 4874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek long duration = (long) (DISAPPEAR_DURATION * Math.min(currentDotSizeFactor, 1.0f)); 4884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.setDuration(duration); 4894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.setStartDelay(startDelay); 4904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.start(); 4914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimator = animator; 4924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimationIsGrowing = false; 4934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void startWidthAppearAnimation() { 4964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(widthAnimator); 4974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator = ValueAnimator.ofFloat(currentWidthFactor, 1.0f); 4984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.addUpdateListener(widthUpdater); 4994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.addListener(widthFinishListener); 5004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.setDuration((long) (APPEAR_DURATION * (1f - currentWidthFactor))); 5014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.start(); 5024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimationIsGrowing = true; 5034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void startTextAppearAnimation() { 5064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(textAnimator); 5074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator = ValueAnimator.ofFloat(currentTextSizeFactor, 1.0f); 5084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.addUpdateListener(textSizeUpdater); 5094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.addListener(textFinishListener); 5104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.setInterpolator(mAppearInterpolator); 5114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.setDuration((long) (APPEAR_DURATION * (1f - currentTextSizeFactor))); 5124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.start(); 5134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimationIsGrowing = true; 5144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek // handle translation 5164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (textTranslateAnimator == null) { 5174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator = ValueAnimator.ofFloat(1.0f, 0.0f); 5184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator.addUpdateListener(textTranslationUpdater); 5194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator.addListener(textTranslateFinishListener); 5204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator.setInterpolator(mAppearInterpolator); 5214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator.setDuration(APPEAR_DURATION); 5224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator.start(); 5234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void startDotAppearAnimation(long delay) { 5274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(dotAnimator); 5284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (!mShowPassword) { 5294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek // We perform an overshoot animation 5304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator overShootAnimator = ValueAnimator.ofFloat(currentDotSizeFactor, 5314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek DOT_OVERSHOOT_FACTOR); 5324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek overShootAnimator.addUpdateListener(dotSizeUpdater); 5334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek overShootAnimator.setInterpolator(mAppearInterpolator); 5344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek long overShootDuration = (long) (DOT_APPEAR_DURATION_OVERSHOOT 5354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * OVERSHOOT_TIME_POSITION); 5364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek overShootAnimator.setDuration(overShootDuration); 5374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator settleBackAnimator = ValueAnimator.ofFloat(DOT_OVERSHOOT_FACTOR, 5384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1.0f); 5394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek settleBackAnimator.addUpdateListener(dotSizeUpdater); 5404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek settleBackAnimator.setDuration(DOT_APPEAR_DURATION_OVERSHOOT - overShootDuration); 5414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek settleBackAnimator.addListener(dotFinishListener); 5424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek AnimatorSet animatorSet = new AnimatorSet(); 5434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animatorSet.playSequentially(overShootAnimator, settleBackAnimator); 5444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animatorSet.setStartDelay(delay); 5454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animatorSet.start(); 5464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimator = animatorSet; 5474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } else { 5484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator growAnimator = ValueAnimator.ofFloat(currentDotSizeFactor, 1.0f); 5494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek growAnimator.addUpdateListener(dotSizeUpdater); 5504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek growAnimator.setDuration((long) (APPEAR_DURATION * (1.0f - currentDotSizeFactor))); 5514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek growAnimator.addListener(dotFinishListener); 5524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek growAnimator.setStartDelay(delay); 5534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek growAnimator.start(); 5544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimator = growAnimator; 5554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimationIsGrowing = true; 5574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void cancelAnimator(Animator animator) { 5604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (animator != null) { 5614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.cancel(); 5624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 5664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * Draw this char to the canvas. 5674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * 5684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * @return The width this character contributes, including padding. 5694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 5704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public float draw(Canvas canvas, float currentDrawPosition, int charHeight, float yPosition, 5714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float charLength) { 5724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean textVisible = currentTextSizeFactor > 0; 5734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean dotVisible = currentDotSizeFactor > 0; 5744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float charWidth = charLength * currentWidthFactor; 5754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (textVisible) { 5764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float currYPosition = yPosition + charHeight / 2.0f * currentTextSizeFactor 5774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek + charHeight * currentTextTranslationY * 0.8f; 5784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.save(); 5794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float centerX = currentDrawPosition + charWidth / 2; 5804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.translate(centerX, currYPosition); 5814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.scale(currentTextSizeFactor, currentTextSizeFactor); 5824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.drawText(Character.toString(whichChar), 0, 0, mDrawPaint); 5834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.restore(); 5844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (dotVisible) { 5864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.save(); 5874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float centerX = currentDrawPosition + charWidth / 2; 5884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.translate(centerX, yPosition); 5894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.drawCircle(0, 0, mDotSize / 2 * currentDotSizeFactor, mDrawPaint); 5904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.restore(); 5914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek return charWidth + mCharPadding * currentWidthFactor; 5934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek} 596