/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.inputmethod.keyboard.internal; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.view.View; import android.view.ViewGroup; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.latin.common.CoordinateUtils; import com.android.inputmethod.latin.utils.ViewLayoutUtils; import java.util.ArrayDeque; import java.util.HashMap; /** * This class controls pop up key previews. This class decides: * - what kind of key previews should be shown. * - where key previews should be placed. * - how key previews should be shown and dismissed. */ public final class KeyPreviewChoreographer { // Free {@link KeyPreviewView} pool that can be used for key preview. private final ArrayDeque mFreeKeyPreviewViews = new ArrayDeque<>(); // Map from {@link Key} to {@link KeyPreviewView} that is currently being displayed as key // preview. private final HashMap mShowingKeyPreviewViews = new HashMap<>(); private final KeyPreviewDrawParams mParams; public KeyPreviewChoreographer(final KeyPreviewDrawParams params) { mParams = params; } public KeyPreviewView getKeyPreviewView(final Key key, final ViewGroup placerView) { KeyPreviewView keyPreviewView = mShowingKeyPreviewViews.remove(key); if (keyPreviewView != null) { return keyPreviewView; } keyPreviewView = mFreeKeyPreviewViews.poll(); if (keyPreviewView != null) { return keyPreviewView; } final Context context = placerView.getContext(); keyPreviewView = new KeyPreviewView(context, null /* attrs */); keyPreviewView.setBackgroundResource(mParams.mPreviewBackgroundResId); placerView.addView(keyPreviewView, ViewLayoutUtils.newLayoutParam(placerView, 0, 0)); return keyPreviewView; } public boolean isShowingKeyPreview(final Key key) { return mShowingKeyPreviewViews.containsKey(key); } public void dismissKeyPreview(final Key key, final boolean withAnimation) { if (key == null) { return; } final KeyPreviewView keyPreviewView = mShowingKeyPreviewViews.get(key); if (keyPreviewView == null) { return; } final Object tag = keyPreviewView.getTag(); if (withAnimation) { if (tag instanceof KeyPreviewAnimators) { final KeyPreviewAnimators animators = (KeyPreviewAnimators)tag; animators.startDismiss(); return; } } // Dismiss preview without animation. mShowingKeyPreviewViews.remove(key); if (tag instanceof Animator) { ((Animator)tag).cancel(); } keyPreviewView.setTag(null); keyPreviewView.setVisibility(View.INVISIBLE); mFreeKeyPreviewViews.add(keyPreviewView); } public void placeAndShowKeyPreview(final Key key, final KeyboardIconsSet iconsSet, final KeyDrawParams drawParams, final int keyboardViewWidth, final int[] keyboardOrigin, final ViewGroup placerView, final boolean withAnimation) { final KeyPreviewView keyPreviewView = getKeyPreviewView(key, placerView); placeKeyPreview( key, keyPreviewView, iconsSet, drawParams, keyboardViewWidth, keyboardOrigin); showKeyPreview(key, keyPreviewView, withAnimation); } private void placeKeyPreview(final Key key, final KeyPreviewView keyPreviewView, final KeyboardIconsSet iconsSet, final KeyDrawParams drawParams, final int keyboardViewWidth, final int[] originCoords) { keyPreviewView.setPreviewVisual(key, iconsSet, drawParams); keyPreviewView.measure( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); mParams.setGeometry(keyPreviewView); final int previewWidth = keyPreviewView.getMeasuredWidth(); final int previewHeight = mParams.mPreviewHeight; final int keyDrawWidth = key.getDrawWidth(); // The key preview is horizontally aligned with the center of the visible part of the // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and // the left/right background is used if such background is specified. final int keyPreviewPosition; int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2 + CoordinateUtils.x(originCoords); if (previewX < 0) { previewX = 0; keyPreviewPosition = KeyPreviewView.POSITION_LEFT; } else if (previewX > keyboardViewWidth - previewWidth) { previewX = keyboardViewWidth - previewWidth; keyPreviewPosition = KeyPreviewView.POSITION_RIGHT; } else { keyPreviewPosition = KeyPreviewView.POSITION_MIDDLE; } final boolean hasMoreKeys = (key.getMoreKeys() != null); keyPreviewView.setPreviewBackground(hasMoreKeys, keyPreviewPosition); // The key preview is placed vertically above the top edge of the parent key with an // arbitrary offset. final int previewY = key.getY() - previewHeight + mParams.mPreviewOffset + CoordinateUtils.y(originCoords); ViewLayoutUtils.placeViewAt( keyPreviewView, previewX, previewY, previewWidth, previewHeight); keyPreviewView.setPivotX(previewWidth / 2.0f); keyPreviewView.setPivotY(previewHeight); } void showKeyPreview(final Key key, final KeyPreviewView keyPreviewView, final boolean withAnimation) { if (!withAnimation) { keyPreviewView.setVisibility(View.VISIBLE); mShowingKeyPreviewViews.put(key, keyPreviewView); return; } // Show preview with animation. final Animator showUpAnimator = createShowUpAnimator(key, keyPreviewView); final Animator dismissAnimator = createDismissAnimator(key, keyPreviewView); final KeyPreviewAnimators animators = new KeyPreviewAnimators( showUpAnimator, dismissAnimator); keyPreviewView.setTag(animators); animators.startShowUp(); } public Animator createShowUpAnimator(final Key key, final KeyPreviewView keyPreviewView) { final Animator showUpAnimator = mParams.createShowUpAnimator(keyPreviewView); showUpAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(final Animator animator) { showKeyPreview(key, keyPreviewView, false /* withAnimation */); } }); return showUpAnimator; } private Animator createDismissAnimator(final Key key, final KeyPreviewView keyPreviewView) { final Animator dismissAnimator = mParams.createDismissAnimator(keyPreviewView); dismissAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(final Animator animator) { dismissKeyPreview(key, false /* withAnimation */); } }); return dismissAnimator; } private static class KeyPreviewAnimators extends AnimatorListenerAdapter { private final Animator mShowUpAnimator; private final Animator mDismissAnimator; public KeyPreviewAnimators(final Animator showUpAnimator, final Animator dismissAnimator) { mShowUpAnimator = showUpAnimator; mDismissAnimator = dismissAnimator; } public void startShowUp() { mShowUpAnimator.start(); } public void startDismiss() { if (mShowUpAnimator.isRunning()) { mShowUpAnimator.addListener(this); return; } mDismissAnimator.start(); } @Override public void onAnimationEnd(final Animator animator) { mDismissAnimator.start(); } } }