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; 32a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roosimport android.text.InputType; 334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.util.AttributeSet; 3490427507882de8ddbd178c6466896deb4969f5bcEvan Roskyimport android.view.Gravity; 354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.view.View; 36a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roosimport android.view.accessibility.AccessibilityEvent; 37a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roosimport android.view.accessibility.AccessibilityManager; 38a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roosimport android.view.accessibility.AccessibilityNodeInfo; 394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.view.animation.AnimationUtils; 404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport android.view.animation.Interpolator; 41385912ee2d78e0e557704cfd5f8c7dbe2b7fd280Phil Weaverimport android.widget.EditText; 424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport java.util.ArrayList; 444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekimport java.util.Stack; 454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek/** 474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * A View similar to a textView which contains password text and can animate when the text is 484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * changed 494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinekpublic class PasswordTextView extends View { 514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final float DOT_OVERSHOOT_FACTOR = 1.5f; 534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long DOT_APPEAR_DURATION_OVERSHOOT = 320; 544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long APPEAR_DURATION = 160; 554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long DISAPPEAR_DURATION = 160; 564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long RESET_DELAY_PER_ELEMENT = 40; 574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long RESET_MAX_DELAY = 200; 584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * The overlap between the text disappearing and the dot appearing animation 614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long DOT_APPEAR_TEXT_DISAPPEAR_OVERLAP_DURATION = 130; 634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * The duration the text needs to stay there at least before it can morph into a dot 664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long TEXT_REST_DURATION_AFTER_APPEAR = 100; 684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * The duration the text should be visible, starting with the appear animation 714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final long TEXT_VISIBILITY_DURATION = 1300; 734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * The position in time from [0,1] where the overshoot should be finished and the settle back 764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * animation of the dot should start 774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private static final float OVERSHOOT_TIME_POSITION = 0.5f; 794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * The raw text size, will be multiplied by the scaled density when drawn 824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private final int mTextHeightRaw; 8490427507882de8ddbd178c6466896deb4969f5bcEvan Rosky private final int mGravity; 854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private ArrayList<CharState> mTextChars = new ArrayList<>(); 864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private String mText = ""; 874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private Stack<CharState> mCharPool = new Stack<>(); 884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private int mDotSize; 894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private PowerManager mPM; 904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private int mCharPadding; 914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private final Paint mDrawPaint = new Paint(); 924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private Interpolator mAppearInterpolator; 934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private Interpolator mDisappearInterpolator; 944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private Interpolator mFastOutSlowInInterpolator; 954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private boolean mShowPassword; 9609eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia private UserActivityListener mUserActivityListener; 9709eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia 9809eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia public interface UserActivityListener { 9909eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia void onUserActivity(); 10009eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia } 1014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public PasswordTextView(Context context) { 1034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek this(context, null); 1044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public PasswordTextView(Context context, AttributeSet attrs) { 1074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek this(context, attrs, 0); 1084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public PasswordTextView(Context context, AttributeSet attrs, int defStyleAttr) { 1114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek this(context, attrs, defStyleAttr, 0); 1124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public PasswordTextView(Context context, AttributeSet attrs, int defStyleAttr, 1154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int defStyleRes) { 1164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek super(context, attrs, defStyleAttr, defStyleRes); 1174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek setFocusableInTouchMode(true); 1184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek setFocusable(true); 1194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PasswordTextView); 1204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek try { 1214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mTextHeightRaw = a.getInt(R.styleable.PasswordTextView_scaledTextSize, 0); 12290427507882de8ddbd178c6466896deb4969f5bcEvan Rosky mGravity = a.getInt(R.styleable.PasswordTextView_android_gravity, Gravity.CENTER); 12390427507882de8ddbd178c6466896deb4969f5bcEvan Rosky mDotSize = a.getDimensionPixelSize(R.styleable.PasswordTextView_dotSize, 12490427507882de8ddbd178c6466896deb4969f5bcEvan Rosky getContext().getResources().getDimensionPixelSize(R.dimen.password_dot_size)); 12590427507882de8ddbd178c6466896deb4969f5bcEvan Rosky mCharPadding = a.getDimensionPixelSize(R.styleable.PasswordTextView_charPadding, 12690427507882de8ddbd178c6466896deb4969f5bcEvan Rosky getContext().getResources().getDimensionPixelSize( 12790427507882de8ddbd178c6466896deb4969f5bcEvan Rosky R.dimen.password_char_padding)); 1284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } finally { 1294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek a.recycle(); 1304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDrawPaint.setFlags(Paint.SUBPIXEL_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG); 1324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDrawPaint.setTextAlign(Paint.Align.CENTER); 1334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDrawPaint.setColor(0xffffffff); 1344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDrawPaint.setTypeface(Typeface.create("sans-serif-light", 0)); 1354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mShowPassword = Settings.System.getInt(mContext.getContentResolver(), 1364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Settings.System.TEXT_SHOW_PASSWORD, 1) == 1; 1374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mAppearInterpolator = AnimationUtils.loadInterpolator(mContext, 1384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek android.R.interpolator.linear_out_slow_in); 1394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext, 1404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek android.R.interpolator.fast_out_linear_in); 1414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext, 1424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek android.R.interpolator.fast_out_slow_in); 1434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 1444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 1474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek protected void onDraw(Canvas canvas) { 1484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float totalDrawingWidth = getDrawingWidth(); 14990427507882de8ddbd178c6466896deb4969f5bcEvan Rosky float currentDrawPosition; 15022d1201c39d734273ed4a356de0df0c082623479Evan Rosky if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT) { 15122d1201c39d734273ed4a356de0df0c082623479Evan Rosky if ((mGravity & Gravity.RELATIVE_LAYOUT_DIRECTION) != 0 15222d1201c39d734273ed4a356de0df0c082623479Evan Rosky && getLayoutDirection() == LAYOUT_DIRECTION_RTL) { 15390427507882de8ddbd178c6466896deb4969f5bcEvan Rosky currentDrawPosition = getWidth() - getPaddingRight() - totalDrawingWidth; 15490427507882de8ddbd178c6466896deb4969f5bcEvan Rosky } else { 15590427507882de8ddbd178c6466896deb4969f5bcEvan Rosky currentDrawPosition = getPaddingLeft(); 15690427507882de8ddbd178c6466896deb4969f5bcEvan Rosky } 15790427507882de8ddbd178c6466896deb4969f5bcEvan Rosky } else { 15890427507882de8ddbd178c6466896deb4969f5bcEvan Rosky currentDrawPosition = getWidth() / 2 - totalDrawingWidth / 2; 15990427507882de8ddbd178c6466896deb4969f5bcEvan Rosky } 1604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int length = mTextChars.size(); 1614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Rect bounds = getCharBounds(); 1624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int charHeight = (bounds.bottom - bounds.top); 16390427507882de8ddbd178c6466896deb4969f5bcEvan Rosky float yPosition = 16490427507882de8ddbd178c6466896deb4969f5bcEvan Rosky (getHeight() - getPaddingBottom() - getPaddingTop()) / 2 + getPaddingTop(); 16590427507882de8ddbd178c6466896deb4969f5bcEvan Rosky canvas.clipRect(getPaddingLeft(), getPaddingTop(), 16622d1201c39d734273ed4a356de0df0c082623479Evan Rosky getWidth() - getPaddingRight(), getHeight() - getPaddingBottom()); 1674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float charLength = bounds.right - bounds.left; 1684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek for (int i = 0; i < length; i++) { 1694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState charState = mTextChars.get(i); 1704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float charWidth = charState.draw(canvas, currentDrawPosition, charHeight, yPosition, 1714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charLength); 1724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentDrawPosition += charWidth; 1734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1766380482d081e557164393417370e0c5da22a5bdaSelim Cinek @Override 1776380482d081e557164393417370e0c5da22a5bdaSelim Cinek public boolean hasOverlappingRendering() { 1786380482d081e557164393417370e0c5da22a5bdaSelim Cinek return false; 1796380482d081e557164393417370e0c5da22a5bdaSelim Cinek } 1806380482d081e557164393417370e0c5da22a5bdaSelim Cinek 1814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private Rect getCharBounds() { 1824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float textHeight = mTextHeightRaw * getResources().getDisplayMetrics().scaledDensity; 1834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDrawPaint.setTextSize(textHeight); 1844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Rect bounds = new Rect(); 1854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mDrawPaint.getTextBounds("0", 0, 1, bounds); 1864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek return bounds; 1874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private float getDrawingWidth() { 1904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int width = 0; 1914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int length = mTextChars.size(); 1924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Rect bounds = getCharBounds(); 1934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int charLength = bounds.right - bounds.left; 1944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek for (int i = 0; i < length; i++) { 1954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState charState = mTextChars.get(i); 1964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (i != 0) { 1974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek width += mCharPadding * charState.currentWidthFactor; 1984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 1994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek width += charLength * charState.currentWidthFactor; 2004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek return width; 2024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void append(char c) { 2064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int visibleChars = mTextChars.size(); 207a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos String textbefore = mText; 2084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mText = mText + c; 2094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int newLength = mText.length(); 2104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState charState; 2114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (newLength > visibleChars) { 2124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState = obtainCharState(c); 2134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mTextChars.add(charState); 2144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } else { 2154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState = mTextChars.get(newLength - 1); 2164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.whichChar = c; 2174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.startAppearAnimation(); 2194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek // ensure that the previous element is being swapped 2214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (newLength > 1) { 2224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState previousState = mTextChars.get(newLength - 2); 2234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (previousState.isDotSwapPending) { 2244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek previousState.swapToDotWhenAppearFinished(); 2254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek userActivity(); 228a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length(), 0, 1); 2294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 23109eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia public void setUserActivityListener(UserActivityListener userActivitiListener) { 23209eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia mUserActivityListener = userActivitiListener; 23309eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia } 23409eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia 2354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void userActivity() { 2364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mPM.userActivity(SystemClock.uptimeMillis(), false); 23709eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia if (mUserActivityListener != null) { 23809eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia mUserActivityListener.onUserActivity(); 23909eb0337b760c74d73a614edfdc7eaa6e083a29fXiyuan Xia } 2404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void deleteLastChar() { 2434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int length = mText.length(); 244a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos String textbefore = mText; 2454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (length > 0) { 2464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mText = mText.substring(0, length - 1); 2474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState charState = mTextChars.get(length - 1); 2484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.startRemoveAnimation(0, 0); 249961a323040acd945cd7e07b9a147cbadf59f238cSelim Cinek sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length() - 1, 1, 0); 2504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek userActivity(); 2524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public String getText() { 2554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek return mText; 2564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private CharState obtainCharState(char c) { 2594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState charState; 2604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if(mCharPool.isEmpty()) { 2614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState = new CharState(); 2624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } else { 2634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState = mCharPool.pop(); 2644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.reset(); 2654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.whichChar = c; 2674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek return charState; 2684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 2704db942c21bac097a4c87b479d07d47cc3d7cc002Jim Miller public void reset(boolean animated, boolean announce) { 271a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos String textbefore = mText; 2724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mText = ""; 2734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int length = mTextChars.size(); 2744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int middleIndex = (length - 1) / 2; 2754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek long delayPerElement = RESET_DELAY_PER_ELEMENT; 2764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek for (int i = 0; i < length; i++) { 2774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek CharState charState = mTextChars.get(i); 2784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (animated) { 2794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int delayIndex; 2804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (i <= middleIndex) { 2814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek delayIndex = i * 2; 2824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } else { 2834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek int distToMiddle = i - middleIndex; 2844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek delayIndex = (length - 1) - (distToMiddle - 1) * 2; 2854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek long startDelay = delayIndex * delayPerElement; 2874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startDelay = Math.min(startDelay, RESET_MAX_DELAY); 2884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek long maxDelay = delayPerElement * (length - 1); 2894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek maxDelay = Math.min(maxDelay, RESET_MAX_DELAY) + DISAPPEAR_DURATION; 2904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.startRemoveAnimation(startDelay, maxDelay); 2914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek charState.removeDotSwapCallbacks(); 2924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } else { 2934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mCharPool.push(charState); 2944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (!animated) { 2974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mTextChars.clear(); 2984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 2994db942c21bac097a4c87b479d07d47cc3d7cc002Jim Miller if (announce) { 3004db942c21bac097a4c87b479d07d47cc3d7cc002Jim Miller sendAccessibilityEventTypeViewTextChanged(textbefore, 0, textbefore.length(), 0); 3014db942c21bac097a4c87b479d07d47cc3d7cc002Jim Miller } 302a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos } 303a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos 304a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos void sendAccessibilityEventTypeViewTextChanged(String beforeText, int fromIndex, 305a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos int removedCount, int addedCount) { 306a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos if (AccessibilityManager.getInstance(mContext).isEnabled() && 307a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos (isFocused() || isSelected() && isShown())) { 308a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos AccessibilityEvent event = 309a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); 310a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos event.setFromIndex(fromIndex); 311a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos event.setRemovedCount(removedCount); 312a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos event.setAddedCount(addedCount); 313a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos event.setBeforeText(beforeText); 314a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos event.setPassword(true); 315a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos sendAccessibilityEventUnchecked(event); 316a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos } 317a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos } 318a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos 319a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos @Override 320a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 321a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos super.onInitializeAccessibilityEvent(event); 322a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos 323385912ee2d78e0e557704cfd5f8c7dbe2b7fd280Phil Weaver event.setClassName(EditText.class.getName()); 324a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos event.setPassword(true); 325a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos } 326a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos 327a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos @Override 328a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 329a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos super.onInitializeAccessibilityNodeInfo(info); 330a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos 331a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos info.setClassName(PasswordTextView.class.getName()); 332a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos info.setPassword(true); 333a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos 334a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos info.setEditable(true); 335a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos 336a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos info.setInputType(InputType.TYPE_NUMBER_VARIATION_PASSWORD); 337a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos } 338a48caee9a717afe80a2210e5db3a4d6dabd952b5Adrian Roos 3394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private class CharState { 3404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek char whichChar; 3414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator textAnimator; 3424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean textAnimationIsGrowing; 3434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Animator dotAnimator; 3444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean dotAnimationIsGrowing; 3454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator widthAnimator; 3464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean widthAnimationIsGrowing; 3474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float currentTextSizeFactor; 3484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float currentDotSizeFactor; 3494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float currentWidthFactor; 3504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean isDotSwapPending; 3514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float currentTextTranslationY = 1.0f; 3524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator textTranslateAnimator; 3534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Animator.AnimatorListener removeEndListener = new AnimatorListenerAdapter() { 3554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private boolean mCancelled; 3564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationCancel(Animator animation) { 3584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mCancelled = true; 3594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationEnd(Animator animation) { 3634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (!mCancelled) { 3644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mTextChars.remove(CharState.this); 3654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mCharPool.push(CharState.this); 3664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek reset(); 3674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(textTranslateAnimator); 3684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator = null; 3694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationStart(Animator animation) { 3744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek mCancelled = false; 3754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 3774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Animator.AnimatorListener dotFinishListener = new AnimatorListenerAdapter() { 3794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationEnd(Animator animation) { 3814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimator = null; 3824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 3844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Animator.AnimatorListener textFinishListener = new AnimatorListenerAdapter() { 3864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationEnd(Animator animation) { 3884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator = null; 3894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 3914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Animator.AnimatorListener textTranslateFinishListener = new AnimatorListenerAdapter() { 3934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 3944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationEnd(Animator animation) { 3954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator = null; 3964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 3974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 3984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 3994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek Animator.AnimatorListener widthFinishListener = new AnimatorListenerAdapter() { 4004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 4014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationEnd(Animator animation) { 4024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator = null; 4034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 4054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private ValueAnimator.AnimatorUpdateListener dotSizeUpdater 4074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek = new ValueAnimator.AnimatorUpdateListener() { 4084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 4094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationUpdate(ValueAnimator animation) { 4104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentDotSizeFactor = (float) animation.getAnimatedValue(); 4114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek invalidate(); 4124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 4144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private ValueAnimator.AnimatorUpdateListener textSizeUpdater 4164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek = new ValueAnimator.AnimatorUpdateListener() { 4174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 4184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationUpdate(ValueAnimator animation) { 4194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentTextSizeFactor = (float) animation.getAnimatedValue(); 4204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek invalidate(); 4214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 4234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private ValueAnimator.AnimatorUpdateListener textTranslationUpdater 4254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek = new ValueAnimator.AnimatorUpdateListener() { 4264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 4274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationUpdate(ValueAnimator animation) { 4284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentTextTranslationY = (float) animation.getAnimatedValue(); 4294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek invalidate(); 4304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 4324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private ValueAnimator.AnimatorUpdateListener widthUpdater 4344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek = new ValueAnimator.AnimatorUpdateListener() { 4354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 4364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void onAnimationUpdate(ValueAnimator animation) { 4374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentWidthFactor = (float) animation.getAnimatedValue(); 4384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek invalidate(); 4394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 4414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private Runnable dotSwapperRunnable = new Runnable() { 4434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek @Override 4444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public void run() { 4454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek performSwap(); 4464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek isDotSwapPending = false; 4474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek }; 4494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek void reset() { 4514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek whichChar = 0; 4524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentTextSizeFactor = 0.0f; 4534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentDotSizeFactor = 0.0f; 4544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentWidthFactor = 0.0f; 4554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(textAnimator); 4564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator = null; 4574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(dotAnimator); 4584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimator = null; 4594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(widthAnimator); 4604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator = null; 4614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek currentTextTranslationY = 1.0f; 4624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek removeDotSwapCallbacks(); 4634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek void startRemoveAnimation(long startDelay, long widthDelay) { 4664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean dotNeedsAnimation = (currentDotSizeFactor > 0.0f && dotAnimator == null) 4674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek || (dotAnimator != null && dotAnimationIsGrowing); 4684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean textNeedsAnimation = (currentTextSizeFactor > 0.0f && textAnimator == null) 4694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek || (textAnimator != null && textAnimationIsGrowing); 4704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean widthNeedsAnimation = (currentWidthFactor > 0.0f && widthAnimator == null) 4714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek || (widthAnimator != null && widthAnimationIsGrowing); 4724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (dotNeedsAnimation) { 4734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startDotDisappearAnimation(startDelay); 4744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (textNeedsAnimation) { 4764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startTextDisappearAnimation(startDelay); 4774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (widthNeedsAnimation) { 4794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startWidthDisappearAnimation(widthDelay); 4804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 4834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek void startAppearAnimation() { 4844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean dotNeedsAnimation = !mShowPassword 4854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek && (dotAnimator == null || !dotAnimationIsGrowing); 4864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean textNeedsAnimation = mShowPassword 4874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek && (textAnimator == null || !textAnimationIsGrowing); 4884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean widthNeedsAnimation = (widthAnimator == null || !widthAnimationIsGrowing); 4894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (dotNeedsAnimation) { 4904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startDotAppearAnimation(0); 4914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (textNeedsAnimation) { 4934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startTextAppearAnimation(); 4944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (widthNeedsAnimation) { 4964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startWidthAppearAnimation(); 4974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 4984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (mShowPassword) { 4994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek postDotSwap(TEXT_VISIBILITY_DURATION); 5004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 5044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * Posts a runnable which ensures that the text will be replaced by a dot after {@link 5054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * com.android.keyguard.PasswordTextView#TEXT_VISIBILITY_DURATION}. 5064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 5074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void postDotSwap(long delay) { 5084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek removeDotSwapCallbacks(); 5094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek postDelayed(dotSwapperRunnable, delay); 5104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek isDotSwapPending = true; 5114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void removeDotSwapCallbacks() { 5144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek removeCallbacks(dotSwapperRunnable); 5154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek isDotSwapPending = false; 5164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek void swapToDotWhenAppearFinished() { 5194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek removeDotSwapCallbacks(); 5204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (textAnimator != null) { 5214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek long remainingDuration = textAnimator.getDuration() 5224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek - textAnimator.getCurrentPlayTime(); 5234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek postDotSwap(remainingDuration + TEXT_REST_DURATION_AFTER_APPEAR); 5244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } else { 5254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek performSwap(); 5264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void performSwap() { 5304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startTextDisappearAnimation(0); 5314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek startDotAppearAnimation(DISAPPEAR_DURATION 5324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek - DOT_APPEAR_TEXT_DISAPPEAR_OVERLAP_DURATION); 5334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void startWidthDisappearAnimation(long widthDelay) { 5364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(widthAnimator); 5374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator = ValueAnimator.ofFloat(currentWidthFactor, 0.0f); 5384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.addUpdateListener(widthUpdater); 5394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.addListener(widthFinishListener); 5404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.addListener(removeEndListener); 5414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.setDuration((long) (DISAPPEAR_DURATION * currentWidthFactor)); 5424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.setStartDelay(widthDelay); 5434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.start(); 5444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimationIsGrowing = false; 5454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void startTextDisappearAnimation(long startDelay) { 5484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(textAnimator); 5494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator = ValueAnimator.ofFloat(currentTextSizeFactor, 0.0f); 5504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.addUpdateListener(textSizeUpdater); 5514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.addListener(textFinishListener); 5524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.setInterpolator(mDisappearInterpolator); 5534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.setDuration((long) (DISAPPEAR_DURATION * currentTextSizeFactor)); 5544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.setStartDelay(startDelay); 5554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.start(); 5564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimationIsGrowing = false; 5574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void startDotDisappearAnimation(long startDelay) { 5604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(dotAnimator); 5614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator animator = ValueAnimator.ofFloat(currentDotSizeFactor, 0.0f); 5624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.addUpdateListener(dotSizeUpdater); 5634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.addListener(dotFinishListener); 5644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.setInterpolator(mDisappearInterpolator); 5654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek long duration = (long) (DISAPPEAR_DURATION * Math.min(currentDotSizeFactor, 1.0f)); 5664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.setDuration(duration); 5674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.setStartDelay(startDelay); 5684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.start(); 5694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimator = animator; 5704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimationIsGrowing = false; 5714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void startWidthAppearAnimation() { 5744e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(widthAnimator); 5754e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator = ValueAnimator.ofFloat(currentWidthFactor, 1.0f); 5764e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.addUpdateListener(widthUpdater); 5774e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.addListener(widthFinishListener); 5784e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.setDuration((long) (APPEAR_DURATION * (1f - currentWidthFactor))); 5794e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimator.start(); 5804e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek widthAnimationIsGrowing = true; 5814e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 5824e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5834e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void startTextAppearAnimation() { 5844e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(textAnimator); 5854e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator = ValueAnimator.ofFloat(currentTextSizeFactor, 1.0f); 5864e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.addUpdateListener(textSizeUpdater); 5874e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.addListener(textFinishListener); 5884e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.setInterpolator(mAppearInterpolator); 5894e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.setDuration((long) (APPEAR_DURATION * (1f - currentTextSizeFactor))); 5904e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimator.start(); 5914e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textAnimationIsGrowing = true; 5924e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 5934e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek // handle translation 5944e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (textTranslateAnimator == null) { 5954e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator = ValueAnimator.ofFloat(1.0f, 0.0f); 5964e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator.addUpdateListener(textTranslationUpdater); 5974e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator.addListener(textTranslateFinishListener); 5984e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator.setInterpolator(mAppearInterpolator); 5994e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator.setDuration(APPEAR_DURATION); 6004e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek textTranslateAnimator.start(); 6014e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 6024e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 6034e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 6044e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void startDotAppearAnimation(long delay) { 6054e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek cancelAnimator(dotAnimator); 6064e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (!mShowPassword) { 6074e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek // We perform an overshoot animation 6084e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator overShootAnimator = ValueAnimator.ofFloat(currentDotSizeFactor, 6094e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek DOT_OVERSHOOT_FACTOR); 6104e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek overShootAnimator.addUpdateListener(dotSizeUpdater); 6114e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek overShootAnimator.setInterpolator(mAppearInterpolator); 6124e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek long overShootDuration = (long) (DOT_APPEAR_DURATION_OVERSHOOT 6134e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * OVERSHOOT_TIME_POSITION); 6144e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek overShootAnimator.setDuration(overShootDuration); 6154e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator settleBackAnimator = ValueAnimator.ofFloat(DOT_OVERSHOOT_FACTOR, 6164e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 1.0f); 6174e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek settleBackAnimator.addUpdateListener(dotSizeUpdater); 6184e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek settleBackAnimator.setDuration(DOT_APPEAR_DURATION_OVERSHOOT - overShootDuration); 6194e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek settleBackAnimator.addListener(dotFinishListener); 6204e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek AnimatorSet animatorSet = new AnimatorSet(); 6214e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animatorSet.playSequentially(overShootAnimator, settleBackAnimator); 6224e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animatorSet.setStartDelay(delay); 6234e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animatorSet.start(); 6244e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimator = animatorSet; 6254e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } else { 6264e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek ValueAnimator growAnimator = ValueAnimator.ofFloat(currentDotSizeFactor, 1.0f); 6274e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek growAnimator.addUpdateListener(dotSizeUpdater); 6284e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek growAnimator.setDuration((long) (APPEAR_DURATION * (1.0f - currentDotSizeFactor))); 6294e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek growAnimator.addListener(dotFinishListener); 6304e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek growAnimator.setStartDelay(delay); 6314e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek growAnimator.start(); 6324e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimator = growAnimator; 6334e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 6344e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek dotAnimationIsGrowing = true; 6354e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 6364e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 6374e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek private void cancelAnimator(Animator animator) { 6384e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (animator != null) { 6394e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek animator.cancel(); 6404e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 6414e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 6424e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek 6434e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek /** 6444e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * Draw this char to the canvas. 6454e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * 6464e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek * @return The width this character contributes, including padding. 6474e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek */ 6484e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek public float draw(Canvas canvas, float currentDrawPosition, int charHeight, float yPosition, 6494e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float charLength) { 6504e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean textVisible = currentTextSizeFactor > 0; 6514e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek boolean dotVisible = currentDotSizeFactor > 0; 6524e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float charWidth = charLength * currentWidthFactor; 6534e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (textVisible) { 6544e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float currYPosition = yPosition + charHeight / 2.0f * currentTextSizeFactor 6554e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek + charHeight * currentTextTranslationY * 0.8f; 6564e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.save(); 6574e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float centerX = currentDrawPosition + charWidth / 2; 6584e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.translate(centerX, currYPosition); 6594e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.scale(currentTextSizeFactor, currentTextSizeFactor); 6604e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.drawText(Character.toString(whichChar), 0, 0, mDrawPaint); 6614e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.restore(); 6624e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 6634e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek if (dotVisible) { 6644e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.save(); 6654e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek float centerX = currentDrawPosition + charWidth / 2; 6664e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.translate(centerX, yPosition); 6674e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.drawCircle(0, 0, mDotSize / 2 * currentDotSizeFactor, mDrawPaint); 6684e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek canvas.restore(); 6694e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 6704e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek return charWidth + mCharPadding * currentWidthFactor; 6714e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 6724e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek } 6734e8b9ed30b67e5449d987e674b2966dc7f3ac224Selim Cinek} 674