Home | History | Annotate | Download | only in internal
      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 
     47     public KeyPreviewChoreographer(final KeyPreviewDrawParams params) {
     48         mParams = params;
     49     }
     50 
     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 
     67     public boolean isShowingKeyPreview(final Key key) {
     68         return mShowingKeyPreviewViews.containsKey(key);
     69     }
     70 
     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 
     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 
    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 
    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 
    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 
    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 
    187         public KeyPreviewAnimators(final Animator showUpAnimator, final Animator dismissAnimator) {
    188             mShowUpAnimator = showUpAnimator;
    189             mDismissAnimator = dismissAnimator;
    190         }
    191 
    192         public void startShowUp() {
    193             mShowUpAnimator.start();
    194         }
    195 
    196         public void startDismiss() {
    197             if (mShowUpAnimator.isRunning()) {
    198                 mShowUpAnimator.addListener(this);
    199                 return;
    200             }
    201             mDismissAnimator.start();
    202         }
    203 
    204         @Override
    205         public void onAnimationEnd(final Animator animator) {
    206             mDismissAnimator.start();
    207         }
    208     }
    209 }
    210