Home | History | Annotate | Download | only in keyboard
      1 /*
      2  * Copyright (C) 2011 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;
     18 
     19 import android.content.Context;
     20 import android.content.res.TypedArray;
     21 import android.graphics.Canvas;
     22 import android.graphics.Paint;
     23 import android.graphics.drawable.Drawable;
     24 import android.util.AttributeSet;
     25 import android.view.MotionEvent;
     26 import android.view.View;
     27 import android.view.ViewGroup;
     28 
     29 import com.android.inputmethod.accessibility.AccessibilityUtils;
     30 import com.android.inputmethod.accessibility.MoreKeysKeyboardAccessibilityDelegate;
     31 import com.android.inputmethod.keyboard.internal.KeyDrawParams;
     32 import com.android.inputmethod.latin.Constants;
     33 import com.android.inputmethod.latin.R;
     34 import com.android.inputmethod.latin.utils.CoordinateUtils;
     35 
     36 /**
     37  * A view that renders a virtual {@link MoreKeysKeyboard}. It handles rendering of keys and
     38  * detecting key presses and touch movements.
     39  */
     40 public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel {
     41     private final int[] mCoordinates = CoordinateUtils.newInstance();
     42 
     43     private final Drawable mDivider;
     44     protected final KeyDetector mKeyDetector;
     45     private Controller mController = EMPTY_CONTROLLER;
     46     protected KeyboardActionListener mListener;
     47     private int mOriginX;
     48     private int mOriginY;
     49     private Key mCurrentKey;
     50 
     51     private int mActivePointerId;
     52 
     53     protected MoreKeysKeyboardAccessibilityDelegate mAccessibilityDelegate;
     54 
     55     public MoreKeysKeyboardView(final Context context, final AttributeSet attrs) {
     56         this(context, attrs, R.attr.moreKeysKeyboardViewStyle);
     57     }
     58 
     59     public MoreKeysKeyboardView(final Context context, final AttributeSet attrs,
     60             final int defStyle) {
     61         super(context, attrs, defStyle);
     62         final TypedArray moreKeysKeyboardViewAttr = context.obtainStyledAttributes(attrs,
     63                 R.styleable.MoreKeysKeyboardView, defStyle, R.style.MoreKeysKeyboardView);
     64         mDivider = moreKeysKeyboardViewAttr.getDrawable(R.styleable.MoreKeysKeyboardView_divider);
     65         if (mDivider != null) {
     66             // TODO: Drawable itself should have an alpha value.
     67             mDivider.setAlpha(128);
     68         }
     69         moreKeysKeyboardViewAttr.recycle();
     70         mKeyDetector = new MoreKeysDetector(getResources().getDimension(
     71                 R.dimen.config_more_keys_keyboard_slide_allowance));
     72     }
     73 
     74     @Override
     75     protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
     76         final Keyboard keyboard = getKeyboard();
     77         if (keyboard != null) {
     78             final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
     79             final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
     80             setMeasuredDimension(width, height);
     81         } else {
     82             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     83         }
     84     }
     85 
     86     @Override
     87     protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
     88             final KeyDrawParams params) {
     89         if (!key.isSpacer() || !(key instanceof MoreKeysKeyboard.MoreKeyDivider)
     90                 || mDivider == null) {
     91             super.onDrawKeyTopVisuals(key, canvas, paint, params);
     92             return;
     93         }
     94         final int keyWidth = key.getDrawWidth();
     95         final int keyHeight = key.getHeight();
     96         final int iconWidth = Math.min(mDivider.getIntrinsicWidth(), keyWidth);
     97         final int iconHeight = mDivider.getIntrinsicHeight();
     98         final int iconX = (keyWidth - iconWidth) / 2; // Align horizontally center
     99         final int iconY = (keyHeight - iconHeight) / 2; // Align vertically center
    100         drawIcon(canvas, mDivider, iconX, iconY, iconWidth, iconHeight);
    101     }
    102 
    103     @Override
    104     public void setKeyboard(final Keyboard keyboard) {
    105         super.setKeyboard(keyboard);
    106         mKeyDetector.setKeyboard(
    107                 keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
    108         if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
    109             if (mAccessibilityDelegate == null) {
    110                 mAccessibilityDelegate = new MoreKeysKeyboardAccessibilityDelegate(
    111                         this, mKeyDetector);
    112                 mAccessibilityDelegate.setOpenAnnounce(R.string.spoken_open_more_keys_keyboard);
    113                 mAccessibilityDelegate.setCloseAnnounce(R.string.spoken_close_more_keys_keyboard);
    114             }
    115             mAccessibilityDelegate.setKeyboard(keyboard);
    116         } else {
    117             mAccessibilityDelegate = null;
    118         }
    119     }
    120 
    121     @Override
    122     public void showMoreKeysPanel(final View parentView, final Controller controller,
    123             final int pointX, final int pointY, final KeyboardActionListener listener) {
    124         mController = controller;
    125         mListener = listener;
    126         final View container = getContainerView();
    127         // The coordinates of panel's left-top corner in parentView's coordinate system.
    128         // We need to consider background drawable paddings.
    129         final int x = pointX - getDefaultCoordX() - container.getPaddingLeft() - getPaddingLeft();
    130         final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom()
    131                 + getPaddingBottom();
    132 
    133         parentView.getLocationInWindow(mCoordinates);
    134         // Ensure the horizontal position of the panel does not extend past the parentView edges.
    135         final int maxX = parentView.getMeasuredWidth() - container.getMeasuredWidth();
    136         final int panelX = Math.max(0, Math.min(maxX, x)) + CoordinateUtils.x(mCoordinates);
    137         final int panelY = y + CoordinateUtils.y(mCoordinates);
    138         container.setX(panelX);
    139         container.setY(panelY);
    140 
    141         mOriginX = x + container.getPaddingLeft();
    142         mOriginY = y + container.getPaddingTop();
    143         controller.onShowMoreKeysPanel(this);
    144         final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
    145         if (accessibilityDelegate != null
    146                 && AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
    147             accessibilityDelegate.onShowMoreKeysKeyboard();
    148         }
    149     }
    150 
    151     /**
    152      * Returns the default x coordinate for showing this panel.
    153      */
    154     protected int getDefaultCoordX() {
    155         return ((MoreKeysKeyboard)getKeyboard()).getDefaultCoordX();
    156     }
    157 
    158     @Override
    159     public void onDownEvent(final int x, final int y, final int pointerId, final long eventTime) {
    160         mActivePointerId = pointerId;
    161         mCurrentKey = detectKey(x, y);
    162     }
    163 
    164     @Override
    165     public void onMoveEvent(final int x, final int y, final int pointerId, final long eventTime) {
    166         if (mActivePointerId != pointerId) {
    167             return;
    168         }
    169         final boolean hasOldKey = (mCurrentKey != null);
    170         mCurrentKey = detectKey(x, y);
    171         if (hasOldKey && mCurrentKey == null) {
    172             // A more keys keyboard is canceled when detecting no key.
    173             mController.onCancelMoreKeysPanel();
    174         }
    175     }
    176 
    177     @Override
    178     public void onUpEvent(final int x, final int y, final int pointerId, final long eventTime) {
    179         if (mActivePointerId != pointerId) {
    180             return;
    181         }
    182         // Calling {@link #detectKey(int,int,int)} here is harmless because the last move event and
    183         // the following up event share the same coordinates.
    184         mCurrentKey = detectKey(x, y);
    185         if (mCurrentKey != null) {
    186             updateReleaseKeyGraphics(mCurrentKey);
    187             onKeyInput(mCurrentKey, x, y);
    188             mCurrentKey = null;
    189         }
    190     }
    191 
    192     /**
    193      * Performs the specific action for this panel when the user presses a key on the panel.
    194      */
    195     protected void onKeyInput(final Key key, final int x, final int y) {
    196         final int code = key.getCode();
    197         if (code == Constants.CODE_OUTPUT_TEXT) {
    198             mListener.onTextInput(mCurrentKey.getOutputText());
    199         } else if (code != Constants.CODE_UNSPECIFIED) {
    200             if (getKeyboard().hasProximityCharsCorrection(code)) {
    201                 mListener.onCodeInput(code, x, y, false /* isKeyRepeat */);
    202             } else {
    203                 mListener.onCodeInput(code, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
    204                         false /* isKeyRepeat */);
    205             }
    206         }
    207     }
    208 
    209     private Key detectKey(int x, int y) {
    210         final Key oldKey = mCurrentKey;
    211         final Key newKey = mKeyDetector.detectHitKey(x, y);
    212         if (newKey == oldKey) {
    213             return newKey;
    214         }
    215         // A new key is detected.
    216         if (oldKey != null) {
    217             updateReleaseKeyGraphics(oldKey);
    218             invalidateKey(oldKey);
    219         }
    220         if (newKey != null) {
    221             updatePressKeyGraphics(newKey);
    222             invalidateKey(newKey);
    223         }
    224         return newKey;
    225     }
    226 
    227     private void updateReleaseKeyGraphics(final Key key) {
    228         key.onReleased();
    229         invalidateKey(key);
    230     }
    231 
    232     private void updatePressKeyGraphics(final Key key) {
    233         key.onPressed();
    234         invalidateKey(key);
    235     }
    236 
    237     @Override
    238     public void dismissMoreKeysPanel() {
    239         if (!isShowingInParent()) {
    240             return;
    241         }
    242         final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
    243         if (accessibilityDelegate != null
    244                 && AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
    245             accessibilityDelegate.onDismissMoreKeysKeyboard();
    246         }
    247         mController.onDismissMoreKeysPanel();
    248     }
    249 
    250     @Override
    251     public int translateX(final int x) {
    252         return x - mOriginX;
    253     }
    254 
    255     @Override
    256     public int translateY(final int y) {
    257         return y - mOriginY;
    258     }
    259 
    260     @Override
    261     public boolean onTouchEvent(final MotionEvent me) {
    262         final int action = me.getActionMasked();
    263         final long eventTime = me.getEventTime();
    264         final int index = me.getActionIndex();
    265         final int x = (int)me.getX(index);
    266         final int y = (int)me.getY(index);
    267         final int pointerId = me.getPointerId(index);
    268         switch (action) {
    269         case MotionEvent.ACTION_DOWN:
    270         case MotionEvent.ACTION_POINTER_DOWN:
    271             onDownEvent(x, y, pointerId, eventTime);
    272             break;
    273         case MotionEvent.ACTION_UP:
    274         case MotionEvent.ACTION_POINTER_UP:
    275             onUpEvent(x, y, pointerId, eventTime);
    276             break;
    277         case MotionEvent.ACTION_MOVE:
    278             onMoveEvent(x, y, pointerId, eventTime);
    279             break;
    280         }
    281         return true;
    282     }
    283 
    284     /**
    285      * {@inheritDoc}
    286      */
    287     @Override
    288     public boolean onHoverEvent(final MotionEvent event) {
    289         final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
    290         if (accessibilityDelegate != null
    291                 && AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
    292             return accessibilityDelegate.onHoverEvent(event);
    293         }
    294         return super.onHoverEvent(event);
    295     }
    296 
    297     private View getContainerView() {
    298         return (View)getParent();
    299     }
    300 
    301     @Override
    302     public void showInParent(final ViewGroup parentView) {
    303         removeFromParent();
    304         parentView.addView(getContainerView());
    305     }
    306 
    307     @Override
    308     public void removeFromParent() {
    309         final View containerView = getContainerView();
    310         final ViewGroup currentParent = (ViewGroup)containerView.getParent();
    311         if (currentParent != null) {
    312             currentParent.removeView(containerView);
    313         }
    314     }
    315 
    316     @Override
    317     public boolean isShowingInParent() {
    318         return (getContainerView().getParent() != null);
    319     }
    320 }
    321