1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.inputmethod.keyboard.internal; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.content.Context; 22 import android.view.View; 23 import android.view.ViewGroup; 24 25 import com.android.inputmethod.keyboard.Key; 26 import com.android.inputmethod.latin.common.CoordinateUtils; 27 import com.android.inputmethod.latin.utils.ViewLayoutUtils; 28 29 import java.util.ArrayDeque; 30 import java.util.HashMap; 31 32 /** 33 * This class controls pop up key previews. This class decides: 34 * - what kind of key previews should be shown. 35 * - where key previews should be placed. 36 * - how key previews should be shown and dismissed. 37 */ 38 public final class KeyPreviewChoreographer { 39 // Free {@link KeyPreviewView} pool that can be used for key preview. 40 private final ArrayDeque<KeyPreviewView> mFreeKeyPreviewViews = new ArrayDeque<>(); 41 // Map from {@link Key} to {@link KeyPreviewView} that is currently being displayed as key 42 // preview. 43 private final HashMap<Key,KeyPreviewView> mShowingKeyPreviewViews = new HashMap<>(); 44 45 private final KeyPreviewDrawParams mParams; 46 KeyPreviewChoreographer(final KeyPreviewDrawParams params)47 public KeyPreviewChoreographer(final KeyPreviewDrawParams params) { 48 mParams = params; 49 } 50 getKeyPreviewView(final Key key, final ViewGroup placerView)51 public KeyPreviewView getKeyPreviewView(final Key key, final ViewGroup placerView) { 52 KeyPreviewView keyPreviewView = mShowingKeyPreviewViews.remove(key); 53 if (keyPreviewView != null) { 54 return keyPreviewView; 55 } 56 keyPreviewView = mFreeKeyPreviewViews.poll(); 57 if (keyPreviewView != null) { 58 return keyPreviewView; 59 } 60 final Context context = placerView.getContext(); 61 keyPreviewView = new KeyPreviewView(context, null /* attrs */); 62 keyPreviewView.setBackgroundResource(mParams.mPreviewBackgroundResId); 63 placerView.addView(keyPreviewView, ViewLayoutUtils.newLayoutParam(placerView, 0, 0)); 64 return keyPreviewView; 65 } 66 isShowingKeyPreview(final Key key)67 public boolean isShowingKeyPreview(final Key key) { 68 return mShowingKeyPreviewViews.containsKey(key); 69 } 70 dismissKeyPreview(final Key key, final boolean withAnimation)71 public void dismissKeyPreview(final Key key, final boolean withAnimation) { 72 if (key == null) { 73 return; 74 } 75 final KeyPreviewView keyPreviewView = mShowingKeyPreviewViews.get(key); 76 if (keyPreviewView == null) { 77 return; 78 } 79 final Object tag = keyPreviewView.getTag(); 80 if (withAnimation) { 81 if (tag instanceof KeyPreviewAnimators) { 82 final KeyPreviewAnimators animators = (KeyPreviewAnimators)tag; 83 animators.startDismiss(); 84 return; 85 } 86 } 87 // Dismiss preview without animation. 88 mShowingKeyPreviewViews.remove(key); 89 if (tag instanceof Animator) { 90 ((Animator)tag).cancel(); 91 } 92 keyPreviewView.setTag(null); 93 keyPreviewView.setVisibility(View.INVISIBLE); 94 mFreeKeyPreviewViews.add(keyPreviewView); 95 } 96 placeAndShowKeyPreview(final Key key, final KeyboardIconsSet iconsSet, final KeyDrawParams drawParams, final int keyboardViewWidth, final int[] keyboardOrigin, final ViewGroup placerView, final boolean withAnimation)97 public void placeAndShowKeyPreview(final Key key, final KeyboardIconsSet iconsSet, 98 final KeyDrawParams drawParams, final int keyboardViewWidth, final int[] keyboardOrigin, 99 final ViewGroup placerView, final boolean withAnimation) { 100 final KeyPreviewView keyPreviewView = getKeyPreviewView(key, placerView); 101 placeKeyPreview( 102 key, keyPreviewView, iconsSet, drawParams, keyboardViewWidth, keyboardOrigin); 103 showKeyPreview(key, keyPreviewView, withAnimation); 104 } 105 placeKeyPreview(final Key key, final KeyPreviewView keyPreviewView, final KeyboardIconsSet iconsSet, final KeyDrawParams drawParams, final int keyboardViewWidth, final int[] originCoords)106 private void placeKeyPreview(final Key key, final KeyPreviewView keyPreviewView, 107 final KeyboardIconsSet iconsSet, final KeyDrawParams drawParams, 108 final int keyboardViewWidth, final int[] originCoords) { 109 keyPreviewView.setPreviewVisual(key, iconsSet, drawParams); 110 keyPreviewView.measure( 111 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 112 mParams.setGeometry(keyPreviewView); 113 final int previewWidth = keyPreviewView.getMeasuredWidth(); 114 final int previewHeight = mParams.mPreviewHeight; 115 final int keyDrawWidth = key.getDrawWidth(); 116 // The key preview is horizontally aligned with the center of the visible part of the 117 // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and 118 // the left/right background is used if such background is specified. 119 final int keyPreviewPosition; 120 int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2 121 + CoordinateUtils.x(originCoords); 122 if (previewX < 0) { 123 previewX = 0; 124 keyPreviewPosition = KeyPreviewView.POSITION_LEFT; 125 } else if (previewX > keyboardViewWidth - previewWidth) { 126 previewX = keyboardViewWidth - previewWidth; 127 keyPreviewPosition = KeyPreviewView.POSITION_RIGHT; 128 } else { 129 keyPreviewPosition = KeyPreviewView.POSITION_MIDDLE; 130 } 131 final boolean hasMoreKeys = (key.getMoreKeys() != null); 132 keyPreviewView.setPreviewBackground(hasMoreKeys, keyPreviewPosition); 133 // The key preview is placed vertically above the top edge of the parent key with an 134 // arbitrary offset. 135 final int previewY = key.getY() - previewHeight + mParams.mPreviewOffset 136 + CoordinateUtils.y(originCoords); 137 138 ViewLayoutUtils.placeViewAt( 139 keyPreviewView, previewX, previewY, previewWidth, previewHeight); 140 keyPreviewView.setPivotX(previewWidth / 2.0f); 141 keyPreviewView.setPivotY(previewHeight); 142 } 143 showKeyPreview(final Key key, final KeyPreviewView keyPreviewView, final boolean withAnimation)144 void showKeyPreview(final Key key, final KeyPreviewView keyPreviewView, 145 final boolean withAnimation) { 146 if (!withAnimation) { 147 keyPreviewView.setVisibility(View.VISIBLE); 148 mShowingKeyPreviewViews.put(key, keyPreviewView); 149 return; 150 } 151 152 // Show preview with animation. 153 final Animator showUpAnimator = createShowUpAnimator(key, keyPreviewView); 154 final Animator dismissAnimator = createDismissAnimator(key, keyPreviewView); 155 final KeyPreviewAnimators animators = new KeyPreviewAnimators( 156 showUpAnimator, dismissAnimator); 157 keyPreviewView.setTag(animators); 158 animators.startShowUp(); 159 } 160 createShowUpAnimator(final Key key, final KeyPreviewView keyPreviewView)161 public Animator createShowUpAnimator(final Key key, final KeyPreviewView keyPreviewView) { 162 final Animator showUpAnimator = mParams.createShowUpAnimator(keyPreviewView); 163 showUpAnimator.addListener(new AnimatorListenerAdapter() { 164 @Override 165 public void onAnimationStart(final Animator animator) { 166 showKeyPreview(key, keyPreviewView, false /* withAnimation */); 167 } 168 }); 169 return showUpAnimator; 170 } 171 createDismissAnimator(final Key key, final KeyPreviewView keyPreviewView)172 private Animator createDismissAnimator(final Key key, final KeyPreviewView keyPreviewView) { 173 final Animator dismissAnimator = mParams.createDismissAnimator(keyPreviewView); 174 dismissAnimator.addListener(new AnimatorListenerAdapter() { 175 @Override 176 public void onAnimationEnd(final Animator animator) { 177 dismissKeyPreview(key, false /* withAnimation */); 178 } 179 }); 180 return dismissAnimator; 181 } 182 183 private static class KeyPreviewAnimators extends AnimatorListenerAdapter { 184 private final Animator mShowUpAnimator; 185 private final Animator mDismissAnimator; 186 KeyPreviewAnimators(final Animator showUpAnimator, final Animator dismissAnimator)187 public KeyPreviewAnimators(final Animator showUpAnimator, final Animator dismissAnimator) { 188 mShowUpAnimator = showUpAnimator; 189 mDismissAnimator = dismissAnimator; 190 } 191 startShowUp()192 public void startShowUp() { 193 mShowUpAnimator.start(); 194 } 195 startDismiss()196 public void startDismiss() { 197 if (mShowUpAnimator.isRunning()) { 198 mShowUpAnimator.addListener(this); 199 return; 200 } 201 mDismissAnimator.start(); 202 } 203 204 @Override onAnimationEnd(final Animator animator)205 public void onAnimationEnd(final Animator animator) { 206 mDismissAnimator.start(); 207 } 208 } 209 } 210