KeyPreviewChoreographer.java revision bb476be4e62b3bed7848d37df42f8fa7363b58d1
1bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka/* 2bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * Copyright (C) 2014 The Android Open Source Project 3bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * 4bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 5bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * you may not use this file except in compliance with the License. 6bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * You may obtain a copy of the License at 7bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * 8bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 9bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * 10bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software 11bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 12bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * See the License for the specific language governing permissions and 14bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * limitations under the License. 15bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka */ 16bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 17bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokapackage com.android.inputmethod.keyboard.internal; 18bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 19bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport android.animation.Animator; 20bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport android.animation.AnimatorListenerAdapter; 21bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport android.animation.AnimatorSet; 22bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport android.animation.ObjectAnimator; 23bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport android.content.Context; 24bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport android.graphics.drawable.Drawable; 25bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport android.util.TypedValue; 26bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport android.view.LayoutInflater; 27bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport android.view.View; 28bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport android.view.ViewGroup; 29bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport android.view.animation.AccelerateInterpolator; 30bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport android.view.animation.DecelerateInterpolator; 31bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport android.widget.TextView; 32bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 33bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Key; 34bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport com.android.inputmethod.latin.R; 35bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport com.android.inputmethod.latin.utils.CollectionUtils; 36bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport com.android.inputmethod.latin.utils.CoordinateUtils; 37bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport com.android.inputmethod.latin.utils.ViewLayoutUtils; 38bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 39bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport java.util.ArrayDeque; 40bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport java.util.HashMap; 41bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport java.util.HashSet; 42bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 43bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka/** 44bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * This class controls pop up key previews. This class decides: 45bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * - what kind of key previews should be shown. 46bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * - where key previews should be placed. 47bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * - how key previews should be shown and dismissed. 48bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka */ 49bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokapublic final class KeyPreviewChoreographer { 50bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // Free {@link TextView} pool that can be used for key preview. 51bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private final ArrayDeque<TextView> mFreeKeyPreviewTextViews = CollectionUtils.newArrayDeque(); 52bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // Map from {@link Key} to {@link TextView} that is currently being displayed as key preview. 53bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private final HashMap<Key,TextView> mShowingKeyPreviewTextViews = CollectionUtils.newHashMap(); 54bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 55bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private final KeyPreviewDrawParams mParams; 56bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 57bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public KeyPreviewChoreographer(final KeyPreviewDrawParams params) { 58bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka mParams = params; 59bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 60bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 61bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public TextView getKeyPreviewTextView(final Key key, final ViewGroup placerView) { 62bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka TextView previewTextView = mShowingKeyPreviewTextViews.remove(key); 63bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (previewTextView != null) { 64bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return previewTextView; 65bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 66bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView = mFreeKeyPreviewTextViews.poll(); 67bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (previewTextView != null) { 68bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return previewTextView; 69bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 70bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final Context context = placerView.getContext(); 71bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (mParams.mLayoutId != 0) { 72bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView = (TextView)LayoutInflater.from(context) 73bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka .inflate(mParams.mLayoutId, null); 74bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } else { 75bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView = new TextView(context); 76bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 77bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka placerView.addView(previewTextView, ViewLayoutUtils.newLayoutParam(placerView, 0, 0)); 78bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return previewTextView; 79bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 80bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 81bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public boolean isShowingKeyPreview(final Key key) { 82bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return mShowingKeyPreviewTextViews.containsKey(key); 83bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 84bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 85bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void dismissAllKeyPreviews() { 86bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka for (final Key key : new HashSet<Key>(mShowingKeyPreviewTextViews.keySet())) { 87bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka dismissKeyPreview(key, false /* withAnimation */); 88bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 89bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 90bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 91bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void dismissKeyPreview(final Key key, final boolean withAnimation) { 92bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (key == null) { 93bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return; 94bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 95bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final TextView previewTextView = mShowingKeyPreviewTextViews.get(key); 96bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (previewTextView == null) { 97bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return; 98bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 99bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final Object tag = previewTextView.getTag(); 100bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (withAnimation) { 101bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (tag instanceof KeyPreviewAnimations) { 102bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final KeyPreviewAnimations animation = (KeyPreviewAnimations)tag; 103bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka animation.startZoomOut(); 104bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 105bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return; 106bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 107bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // Dismiss preview without animation. 108bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (tag instanceof Animator) { 109bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka ((Animator)tag).cancel(); 110bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 111bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setTag(null); 112bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setVisibility(View.INVISIBLE); 113bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka mFreeKeyPreviewTextViews.add(previewTextView); 114bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 115bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 116bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // Background state set 117bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static final int[][][] KEY_PREVIEW_BACKGROUND_STATE_TABLE = { 118bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka { // STATE_MIDDLE 119bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka {}, 120bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka { R.attr.state_has_morekeys } 121bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka }, 122bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka { // STATE_LEFT 123bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka { R.attr.state_left_edge }, 124bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka { R.attr.state_left_edge, R.attr.state_has_morekeys } 125bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka }, 126bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka { // STATE_RIGHT 127bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka { R.attr.state_right_edge }, 128bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka { R.attr.state_right_edge, R.attr.state_has_morekeys } 129bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 130bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka }; 131bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static final int STATE_MIDDLE = 0; 132bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static final int STATE_LEFT = 1; 133bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static final int STATE_RIGHT = 2; 134bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static final int STATE_NORMAL = 0; 135bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static final int STATE_HAS_MOREKEYS = 1; 136bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 137bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void placeKeyPreview(final Key key, final TextView previewTextView, 138bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final KeyboardIconsSet iconsSet, final KeyDrawParams drawParams, 139bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int keyboardViewWidth, final int[] originCoords) { 140bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setTextColor(drawParams.mPreviewTextColor); 141bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final Drawable background = previewTextView.getBackground(); 142bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final String label = key.getPreviewLabel(); 143bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // What we show as preview should match what we show on a key top in onDraw(). 144bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (label != null) { 145bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // TODO Should take care of temporaryShiftLabel here. 146bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setCompoundDrawables(null, null, null, null); 147bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, 148bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka key.selectPreviewTextSize(drawParams)); 149bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setTypeface(key.selectPreviewTypeface(drawParams)); 150bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setText(label); 151bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } else { 152bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setCompoundDrawables(null, null, null, key.getPreviewIcon(iconsSet)); 153bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setText(null); 154bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 155bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 156bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.measure( 157bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 158bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka mParams.setGeometry(previewTextView); 159bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int previewWidth = previewTextView.getMeasuredWidth(); 160bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int previewHeight = mParams.mPreviewHeight; 161bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int keyDrawWidth = key.getDrawWidth(); 162bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // The key preview is horizontally aligned with the center of the visible part of the 163bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and 164bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // the left/right background is used if such background is specified. 165bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int statePosition; 166bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2 167bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka + CoordinateUtils.x(originCoords); 168bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (previewX < 0) { 169bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewX = 0; 170bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka statePosition = STATE_LEFT; 171bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } else if (previewX > keyboardViewWidth - previewWidth) { 172bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewX = keyboardViewWidth - previewWidth; 173bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka statePosition = STATE_RIGHT; 174bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } else { 175bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka statePosition = STATE_MIDDLE; 176bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 177bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // The key preview is placed vertically above the top edge of the parent key with an 178bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // arbitrary offset. 179bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int previewY = key.getY() - previewHeight + mParams.mPreviewOffset 180bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka + CoordinateUtils.y(originCoords); 181bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 182bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (background != null) { 183bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int hasMoreKeys = (key.getMoreKeys() != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL; 184bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]); 185bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 186bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka ViewLayoutUtils.placeViewAt( 187bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView, previewX, previewY, previewWidth, previewHeight); 188bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setPivotX(previewWidth / 2.0f); 189bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setPivotY(previewHeight); 190bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 191bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 192bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void showKeyPreview(final Key key, final TextView previewTextView, 193bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final boolean withAnimation) { 194bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (!withAnimation) { 195bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setVisibility(View.VISIBLE); 196bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka mShowingKeyPreviewTextViews.put(key, previewTextView); 197bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return; 198bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 199bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 200bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // Show preview with animation. 201bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final Animator zoomIn = createZoomInAniation(key, previewTextView); 202bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final Animator zoomOut = createZoomOutAnimation(key, previewTextView); 203bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final KeyPreviewAnimations animation = new KeyPreviewAnimations(zoomIn, zoomOut); 204bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setTag(animation); 205bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka animation.startZoomIn(); 206bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 207bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 208bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // TODO: Move these parameters to resources or preferences. 209bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static final float KEY_PREVIEW_START_ZOOM_IN_SCALE = 0.7f; 210bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static final float KEY_PREVIEW_END_ZOOM_IN_SCALE = 1.0f; 211bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static final float KEY_PREVIEW_END_ZOOM_OUT_SCALE = 0.7f; 212bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = 213bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka new AccelerateInterpolator(); 214bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = 215bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka new DecelerateInterpolator(); 216bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 217bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private Animator createZoomInAniation(final Key key, final TextView previewTextView) { 218bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat( 219bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView, View.SCALE_X, KEY_PREVIEW_START_ZOOM_IN_SCALE, 220bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka KEY_PREVIEW_END_ZOOM_IN_SCALE); 221bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat( 222bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView, View.SCALE_Y, KEY_PREVIEW_START_ZOOM_IN_SCALE, 223bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka KEY_PREVIEW_END_ZOOM_IN_SCALE); 224bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final AnimatorSet zoomInAnimation = new AnimatorSet(); 225bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka zoomInAnimation.play(scaleXAnimation).with(scaleYAnimation); 226bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // TODO: Implement preference option to control key preview animation duration. 227bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka zoomInAnimation.setDuration(mParams.mZoomInDuration); 228bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka zoomInAnimation.setInterpolator(DECELERATE_INTERPOLATOR); 229bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka zoomInAnimation.addListener(new AnimatorListenerAdapter() { 230bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka @Override 231bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void onAnimationStart(final Animator animation) { 232bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka showKeyPreview(key, previewTextView, false /* withAnimation */); 233bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 234bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka }); 235bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return zoomInAnimation; 236bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 237bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 238bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private Animator createZoomOutAnimation(final Key key, final TextView previewTextView) { 239bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat( 240bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView, View.SCALE_X, KEY_PREVIEW_END_ZOOM_OUT_SCALE); 241bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat( 242bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView, View.SCALE_Y, KEY_PREVIEW_END_ZOOM_OUT_SCALE); 243bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final AnimatorSet zoomOutAnimation = new AnimatorSet(); 244bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka zoomOutAnimation.play(scaleXAnimation).with(scaleYAnimation); 245bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // TODO: Implement preference option to control key preview animation duration. 246bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int zoomOutDuration = Math.min(mParams.mZoomOutDuration, mParams.getLingerTimeout()); 247bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka zoomOutAnimation.setDuration(zoomOutDuration); 248bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka zoomOutAnimation.setInterpolator(ACCELERATE_INTERPOLATOR); 249bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka zoomOutAnimation.addListener(new AnimatorListenerAdapter() { 250bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka @Override 251bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void onAnimationEnd(final Animator animation) { 252bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka dismissKeyPreview(key, false /* withAnimation */); 253bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 254bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka }); 255bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return zoomOutAnimation; 256bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 257bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 258bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static class KeyPreviewAnimations extends AnimatorListenerAdapter { 259bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private final Animator mZoomIn; 260bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private final Animator mZoomOut; 261bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 262bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public KeyPreviewAnimations(final Animator zoomIn, final Animator zoomOut) { 263bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka mZoomIn = zoomIn; 264bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka mZoomOut = zoomOut; 265bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 266bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 267bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void startZoomIn() { 268bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka mZoomIn.start(); 269bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 270bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 271bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void startZoomOut() { 272bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (mZoomIn.isRunning()) { 273bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka mZoomIn.addListener(this); 274bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return; 275bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 276bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka mZoomOut.start(); 277bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 278bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 279bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka @Override 280bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void onAnimationEnd(final Animator animation) { 281bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka mZoomOut.start(); 282bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 283bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 284bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka} 285