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