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.utils.CoordinateUtils;
     27 import com.android.inputmethod.latin.utils.ViewLayoutUtils;
     28 
     29 import java.util.ArrayDeque;
     30 import java.util.HashMap;
     31 import java.util.HashSet;
     32 
     33 /**
     34  * This class controls pop up key previews. This class decides:
     35  * - what kind of key previews should be shown.
     36  * - where key previews should be placed.
     37  * - how key previews should be shown and dismissed.
     38  */
     39 public final class KeyPreviewChoreographer {
     40     // Free {@link KeyPreviewView} pool that can be used for key preview.
     41     private final ArrayDeque<KeyPreviewView> mFreeKeyPreviewViews = new ArrayDeque<>();
     42     // Map from {@link Key} to {@link KeyPreviewView} that is currently being displayed as key
     43     // preview.
     44     private final HashMap<Key,KeyPreviewView> mShowingKeyPreviewViews = new HashMap<>();
     45 
     46     private final KeyPreviewDrawParams mParams;
     47 
     48     public KeyPreviewChoreographer(final KeyPreviewDrawParams params) {
     49         mParams = params;
     50     }
     51 
     52     public KeyPreviewView getKeyPreviewView(final Key key, final ViewGroup placerView) {
     53         KeyPreviewView keyPreviewView = mShowingKeyPreviewViews.remove(key);
     54         if (keyPreviewView != null) {
     55             return keyPreviewView;
     56         }
     57         keyPreviewView = mFreeKeyPreviewViews.poll();
     58         if (keyPreviewView != null) {
     59             return keyPreviewView;
     60         }
     61         final Context context = placerView.getContext();
     62         keyPreviewView = new KeyPreviewView(context, null /* attrs */);
     63         keyPreviewView.setBackgroundResource(mParams.mPreviewBackgroundResId);
     64         placerView.addView(keyPreviewView, ViewLayoutUtils.newLayoutParam(placerView, 0, 0));
     65         return keyPreviewView;
     66     }
     67 
     68     public boolean isShowingKeyPreview(final Key key) {
     69         return mShowingKeyPreviewViews.containsKey(key);
     70     }
     71 
     72     public void dismissAllKeyPreviews() {
     73         for (final Key key : new HashSet<>(mShowingKeyPreviewViews.keySet())) {
     74             dismissKeyPreview(key, false /* withAnimation */);
     75         }
     76     }
     77 
     78     public void dismissKeyPreview(final Key key, final boolean withAnimation) {
     79         if (key == null) {
     80             return;
     81         }
     82         final KeyPreviewView keyPreviewView = mShowingKeyPreviewViews.get(key);
     83         if (keyPreviewView == null) {
     84             return;
     85         }
     86         final Object tag = keyPreviewView.getTag();
     87         if (withAnimation) {
     88             if (tag instanceof KeyPreviewAnimators) {
     89                 final KeyPreviewAnimators animators = (KeyPreviewAnimators)tag;
     90                 animators.startDismiss();
     91                 return;
     92             }
     93         }
     94         // Dismiss preview without animation.
     95         mShowingKeyPreviewViews.remove(key);
     96         if (tag instanceof Animator) {
     97             ((Animator)tag).cancel();
     98         }
     99         keyPreviewView.setTag(null);
    100         keyPreviewView.setVisibility(View.INVISIBLE);
    101         mFreeKeyPreviewViews.add(keyPreviewView);
    102     }
    103 
    104     public void placeAndShowKeyPreview(final Key key, final KeyboardIconsSet iconsSet,
    105             final KeyDrawParams drawParams, final int keyboardViewWidth, final int[] keyboardOrigin,
    106             final ViewGroup placerView, final boolean withAnimation) {
    107         final KeyPreviewView keyPreviewView = getKeyPreviewView(key, placerView);
    108         placeKeyPreview(
    109                 key, keyPreviewView, iconsSet, drawParams, keyboardViewWidth, keyboardOrigin);
    110         showKeyPreview(key, keyPreviewView, withAnimation);
    111     }
    112 
    113     private void placeKeyPreview(final Key key, final KeyPreviewView keyPreviewView,
    114             final KeyboardIconsSet iconsSet, final KeyDrawParams drawParams,
    115             final int keyboardViewWidth, final int[] originCoords) {
    116         keyPreviewView.setPreviewVisual(key, iconsSet, drawParams);
    117         keyPreviewView.measure(
    118                 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    119         mParams.setGeometry(keyPreviewView);
    120         final int previewWidth = keyPreviewView.getMeasuredWidth();
    121         final int previewHeight = mParams.mPreviewHeight;
    122         final int keyDrawWidth = key.getDrawWidth();
    123         // The key preview is horizontally aligned with the center of the visible part of the
    124         // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and
    125         // the left/right background is used if such background is specified.
    126         final int keyPreviewPosition;
    127         int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2
    128                 + CoordinateUtils.x(originCoords);
    129         if (previewX < 0) {
    130             previewX = 0;
    131             keyPreviewPosition = KeyPreviewView.POSITION_LEFT;
    132         } else if (previewX > keyboardViewWidth - previewWidth) {
    133             previewX = keyboardViewWidth - previewWidth;
    134             keyPreviewPosition = KeyPreviewView.POSITION_RIGHT;
    135         } else {
    136             keyPreviewPosition = KeyPreviewView.POSITION_MIDDLE;
    137         }
    138         final boolean hasMoreKeys = (key.getMoreKeys() != null);
    139         keyPreviewView.setPreviewBackground(hasMoreKeys, keyPreviewPosition);
    140         // The key preview is placed vertically above the top edge of the parent key with an
    141         // arbitrary offset.
    142         final int previewY = key.getY() - previewHeight + mParams.mPreviewOffset
    143                 + CoordinateUtils.y(originCoords);
    144 
    145         ViewLayoutUtils.placeViewAt(
    146                 keyPreviewView, previewX, previewY, previewWidth, previewHeight);
    147         keyPreviewView.setPivotX(previewWidth / 2.0f);
    148         keyPreviewView.setPivotY(previewHeight);
    149     }
    150 
    151     private void showKeyPreview(final Key key, final KeyPreviewView keyPreviewView,
    152             final boolean withAnimation) {
    153         if (!withAnimation) {
    154             keyPreviewView.setVisibility(View.VISIBLE);
    155             mShowingKeyPreviewViews.put(key, keyPreviewView);
    156             return;
    157         }
    158 
    159         // Show preview with animation.
    160         final Animator showUpAnimator = createShowUpAnimator(key, keyPreviewView);
    161         final Animator dismissAnimator = createDismissAnimator(key, keyPreviewView);
    162         final KeyPreviewAnimators animators = new KeyPreviewAnimators(
    163                 showUpAnimator, dismissAnimator);
    164         keyPreviewView.setTag(animators);
    165         animators.startShowUp();
    166     }
    167 
    168     public Animator createShowUpAnimator(final Key key, final KeyPreviewView keyPreviewView) {
    169         final Animator animator = mParams.createShowUpAnimator(keyPreviewView);
    170         animator.addListener(new AnimatorListenerAdapter() {
    171             @Override
    172             public void onAnimationStart(final Animator animator) {
    173                 showKeyPreview(key, keyPreviewView, false /* withAnimation */);
    174             }
    175         });
    176         return animator;
    177     }
    178 
    179     private Animator createDismissAnimator(final Key key, final KeyPreviewView keyPreviewView) {
    180         final Animator animator = mParams.createDismissAnimator(keyPreviewView);
    181         animator.addListener(new AnimatorListenerAdapter() {
    182             @Override
    183             public void onAnimationEnd(final Animator animator) {
    184                 dismissKeyPreview(key, false /* withAnimation */);
    185             }
    186         });
    187         return animator;
    188     }
    189 
    190     private static class KeyPreviewAnimators extends AnimatorListenerAdapter {
    191         private final Animator mShowUpAnimator;
    192         private final Animator mDismissAnimator;
    193 
    194         public KeyPreviewAnimators(final Animator showUpAnimator, final Animator dismissAnimator) {
    195             mShowUpAnimator = showUpAnimator;
    196             mDismissAnimator = dismissAnimator;
    197         }
    198 
    199         public void startShowUp() {
    200             mShowUpAnimator.start();
    201         }
    202 
    203         public void startDismiss() {
    204             if (mShowUpAnimator.isRunning()) {
    205                 mShowUpAnimator.addListener(this);
    206                 return;
    207             }
    208             mDismissAnimator.start();
    209         }
    210 
    211         @Override
    212         public void onAnimationEnd(final Animator animator) {
    213             mDismissAnimator.start();
    214         }
    215     }
    216 }
    217