KeyPreviewChoreographer.java revision 3ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34f
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.CoordinateUtils; 36bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport com.android.inputmethod.latin.utils.ViewLayoutUtils; 37bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 38bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport java.util.ArrayDeque; 39bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport java.util.HashMap; 40bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport java.util.HashSet; 41bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 42bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka/** 43bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * This class controls pop up key previews. This class decides: 44bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * - what kind of key previews should be shown. 45bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * - where key previews should be placed. 46bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka * - how key previews should be shown and dismissed. 47bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka */ 48bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokapublic final class KeyPreviewChoreographer { 49bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // Free {@link TextView} pool that can be used for key preview. 50a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka private final ArrayDeque<TextView> mFreeKeyPreviewTextViews = new ArrayDeque<>(); 51bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // Map from {@link Key} to {@link TextView} that is currently being displayed as key preview. 52a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka private final HashMap<Key,TextView> mShowingKeyPreviewTextViews = new HashMap<>(); 53bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 54bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private final KeyPreviewDrawParams mParams; 55bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 56bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public KeyPreviewChoreographer(final KeyPreviewDrawParams params) { 57bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka mParams = params; 58bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 59bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 603ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34fTadashi G. Takaoka private TextView getKeyPreviewTextView(final Key key, final ViewGroup placerView) { 61bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka TextView previewTextView = mShowingKeyPreviewTextViews.remove(key); 62bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (previewTextView != null) { 63bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return previewTextView; 64bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 65bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView = mFreeKeyPreviewTextViews.poll(); 66bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (previewTextView != null) { 67bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return previewTextView; 68bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 69bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final Context context = placerView.getContext(); 70bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (mParams.mLayoutId != 0) { 71bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView = (TextView)LayoutInflater.from(context) 72bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka .inflate(mParams.mLayoutId, null); 73bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } else { 74bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView = new TextView(context); 75bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 76bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka placerView.addView(previewTextView, ViewLayoutUtils.newLayoutParam(placerView, 0, 0)); 77bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return previewTextView; 78bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 79bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 80bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public boolean isShowingKeyPreview(final Key key) { 81bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return mShowingKeyPreviewTextViews.containsKey(key); 82bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 83bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 84bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void dismissAllKeyPreviews() { 85a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka for (final Key key : new HashSet<>(mShowingKeyPreviewTextViews.keySet())) { 86bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka dismissKeyPreview(key, false /* withAnimation */); 87bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 88bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 89bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 90bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void dismissKeyPreview(final Key key, final boolean withAnimation) { 91bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (key == null) { 92bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return; 93bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 94bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final TextView previewTextView = mShowingKeyPreviewTextViews.get(key); 95bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (previewTextView == null) { 96bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return; 97bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 98bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final Object tag = previewTextView.getTag(); 99bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (withAnimation) { 100bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (tag instanceof KeyPreviewAnimations) { 101bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final KeyPreviewAnimations animation = (KeyPreviewAnimations)tag; 102276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka animation.startDismiss(); 1037e9b0b42bfd5c51f4bc236a73df3f14cf583c989Tadashi G. Takaoka return; 104bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 105bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 106bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // Dismiss preview without animation. 1077e9b0b42bfd5c51f4bc236a73df3f14cf583c989Tadashi G. Takaoka mShowingKeyPreviewTextViews.remove(key); 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 1373ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34fTadashi G. Takaoka public void placeKeyPreviewAndShow(final Key key, final KeyboardIconsSet iconsSet, 1383ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34fTadashi G. Takaoka final KeyDrawParams drawParams, final int keyboardViewWidth, final int[] keyboardOrigin, 1393ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34fTadashi G. Takaoka final ViewGroup placerView, final boolean withAnimation) { 1403ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34fTadashi G. Takaoka final TextView previewTextView = getKeyPreviewTextView(key, placerView); 1413ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34fTadashi G. Takaoka placeKeyPreview( 1423ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34fTadashi G. Takaoka key, previewTextView, iconsSet, drawParams, keyboardViewWidth, keyboardOrigin); 1433ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34fTadashi G. Takaoka showKeyPreview(key, previewTextView, withAnimation); 1443ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34fTadashi G. Takaoka } 1453ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34fTadashi G. Takaoka 1463ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34fTadashi G. Takaoka private void placeKeyPreview(final Key key, final TextView previewTextView, 147bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final KeyboardIconsSet iconsSet, final KeyDrawParams drawParams, 148bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int keyboardViewWidth, final int[] originCoords) { 149bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setTextColor(drawParams.mPreviewTextColor); 150bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final Drawable background = previewTextView.getBackground(); 151bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final String label = key.getPreviewLabel(); 152bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // What we show as preview should match what we show on a key top in onDraw(). 153bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (label != null) { 154bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // TODO Should take care of temporaryShiftLabel here. 155bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setCompoundDrawables(null, null, null, null); 156bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, 157bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka key.selectPreviewTextSize(drawParams)); 158bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setTypeface(key.selectPreviewTypeface(drawParams)); 159bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setText(label); 160bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } else { 161bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setCompoundDrawables(null, null, null, key.getPreviewIcon(iconsSet)); 162bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setText(null); 163bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 164bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 165bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.measure( 166bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 167bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka mParams.setGeometry(previewTextView); 168bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int previewWidth = previewTextView.getMeasuredWidth(); 169bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int previewHeight = mParams.mPreviewHeight; 170bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int keyDrawWidth = key.getDrawWidth(); 171bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // The key preview is horizontally aligned with the center of the visible part of the 172bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and 173bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // the left/right background is used if such background is specified. 174bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int statePosition; 175bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2 176bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka + CoordinateUtils.x(originCoords); 177bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (previewX < 0) { 178bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewX = 0; 179bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka statePosition = STATE_LEFT; 180bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } else if (previewX > keyboardViewWidth - previewWidth) { 181bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewX = keyboardViewWidth - previewWidth; 182bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka statePosition = STATE_RIGHT; 183bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } else { 184bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka statePosition = STATE_MIDDLE; 185bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 186bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // The key preview is placed vertically above the top edge of the parent key with an 187bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // arbitrary offset. 188bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int previewY = key.getY() - previewHeight + mParams.mPreviewOffset 189bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka + CoordinateUtils.y(originCoords); 190bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 191bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (background != null) { 192bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final int hasMoreKeys = (key.getMoreKeys() != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL; 193bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]); 194bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 195bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka ViewLayoutUtils.placeViewAt( 196bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView, previewX, previewY, previewWidth, previewHeight); 197bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setPivotX(previewWidth / 2.0f); 198bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setPivotY(previewHeight); 199bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 200bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 2013ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34fTadashi G. Takaoka private void showKeyPreview(final Key key, final TextView previewTextView, 202bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final boolean withAnimation) { 203bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka if (!withAnimation) { 204bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setVisibility(View.VISIBLE); 205bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka mShowingKeyPreviewTextViews.put(key, previewTextView); 206bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return; 207bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 208bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 209bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka // Show preview with animation. 210276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka final Animator showUpAnimation = createShowUpAniation(key, previewTextView); 211276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka final Animator dismissAnimation = createDismissAnimation(key, previewTextView); 212276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka final KeyPreviewAnimations animation = new KeyPreviewAnimations( 213276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka showUpAnimation, dismissAnimation); 214bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka previewTextView.setTag(animation); 215276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka animation.startShowUp(); 216bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 217bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 218276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka private static final float KEY_PREVIEW_SHOW_UP_END_SCALE = 1.0f; 219bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = 220bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka new AccelerateInterpolator(); 221bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = 222bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka new DecelerateInterpolator(); 223bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 224276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka private Animator createShowUpAniation(final Key key, final TextView previewTextView) { 225276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka // TODO: Optimization for no scale animation and no duration. 226bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat( 227276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka previewTextView, View.SCALE_X, mParams.getShowUpStartScale(), 228276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka KEY_PREVIEW_SHOW_UP_END_SCALE); 229bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat( 230276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka previewTextView, View.SCALE_Y, mParams.getShowUpStartScale(), 231276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka KEY_PREVIEW_SHOW_UP_END_SCALE); 232276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka final AnimatorSet showUpAnimation = new AnimatorSet(); 233276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka showUpAnimation.play(scaleXAnimation).with(scaleYAnimation); 234276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka showUpAnimation.setDuration(mParams.getShowUpDuration()); 235276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka showUpAnimation.setInterpolator(DECELERATE_INTERPOLATOR); 236276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka showUpAnimation.addListener(new AnimatorListenerAdapter() { 237bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka @Override 238bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void onAnimationStart(final Animator animation) { 239bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka showKeyPreview(key, previewTextView, false /* withAnimation */); 240bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 241bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka }); 242276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka return showUpAnimation; 243bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 244bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 245276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka private Animator createDismissAnimation(final Key key, final TextView previewTextView) { 246276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka // TODO: Optimization for no scale animation and no duration. 247bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat( 248276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka previewTextView, View.SCALE_X, mParams.getDismissEndScale()); 249bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat( 250276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka previewTextView, View.SCALE_Y, mParams.getDismissEndScale()); 251276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka final AnimatorSet dismissAnimation = new AnimatorSet(); 252276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka dismissAnimation.play(scaleXAnimation).with(scaleYAnimation); 253276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka final int dismissDuration = Math.min( 254276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka mParams.getDismissDuration(), mParams.getLingerTimeout()); 255276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka dismissAnimation.setDuration(dismissDuration); 256276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka dismissAnimation.setInterpolator(ACCELERATE_INTERPOLATOR); 257276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka dismissAnimation.addListener(new AnimatorListenerAdapter() { 258bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka @Override 259bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void onAnimationEnd(final Animator animation) { 260bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka dismissKeyPreview(key, false /* withAnimation */); 261bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 262bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka }); 263276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka return dismissAnimation; 264bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 265bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 266bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka private static class KeyPreviewAnimations extends AnimatorListenerAdapter { 267276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka private final Animator mShowUpAnimation; 268276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka private final Animator mDismissAnimation; 269bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 270276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka public KeyPreviewAnimations(final Animator showUpAnimation, 271276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka final Animator dismissAnimation) { 272276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka mShowUpAnimation = showUpAnimation; 273276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka mDismissAnimation = dismissAnimation; 274bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 275bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 276276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka public void startShowUp() { 277276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka mShowUpAnimation.start(); 278bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 279bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 280276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka public void startDismiss() { 281276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka if (mShowUpAnimation.isRunning()) { 282276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka mShowUpAnimation.addListener(this); 283bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka return; 284bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 285276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka mDismissAnimation.start(); 286bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 287bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka 288bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka @Override 289bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka public void onAnimationEnd(final Animator animation) { 290276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka mDismissAnimation.start(); 291bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 292bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka } 293bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka} 294