MoreKeysKeyboardView.java revision 727e818e95ef68460ebafb2eb3b11c23a2d8fcd8
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 17package com.android.inputmethod.keyboard; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.graphics.Canvas; 22import android.graphics.Paint; 23import android.graphics.drawable.Drawable; 24import android.util.AttributeSet; 25import android.view.MotionEvent; 26import android.view.View; 27import android.view.ViewGroup; 28 29import com.android.inputmethod.accessibility.AccessibilityUtils; 30import com.android.inputmethod.accessibility.MoreKeysKeyboardAccessibilityDelegate; 31import com.android.inputmethod.keyboard.internal.KeyDrawParams; 32import com.android.inputmethod.latin.Constants; 33import com.android.inputmethod.latin.R; 34import 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 */ 40public 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().isTouchExplorationEnabled()) { 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 accessibilityDelegate.onShowMoreKeysKeyboard(); 147 } 148 } 149 150 /** 151 * Returns the default x coordinate for showing this panel. 152 */ 153 protected int getDefaultCoordX() { 154 return ((MoreKeysKeyboard)getKeyboard()).getDefaultCoordX(); 155 } 156 157 @Override 158 public void onDownEvent(final int x, final int y, final int pointerId, final long eventTime) { 159 mActivePointerId = pointerId; 160 mCurrentKey = detectKey(x, y); 161 } 162 163 @Override 164 public void onMoveEvent(final int x, final int y, final int pointerId, final long eventTime) { 165 if (mActivePointerId != pointerId) { 166 return; 167 } 168 final boolean hasOldKey = (mCurrentKey != null); 169 mCurrentKey = detectKey(x, y); 170 if (hasOldKey && mCurrentKey == null) { 171 // A more keys keyboard is canceled when detecting no key. 172 mController.onCancelMoreKeysPanel(); 173 } 174 } 175 176 @Override 177 public void onUpEvent(final int x, final int y, final int pointerId, final long eventTime) { 178 if (mActivePointerId != pointerId) { 179 return; 180 } 181 // Calling {@link #detectKey(int,int,int)} here is harmless because the last move event and 182 // the following up event share the same coordinates. 183 mCurrentKey = detectKey(x, y); 184 if (mCurrentKey != null) { 185 updateReleaseKeyGraphics(mCurrentKey); 186 onKeyInput(mCurrentKey, x, y); 187 mCurrentKey = null; 188 } 189 } 190 191 /** 192 * Performs the specific action for this panel when the user presses a key on the panel. 193 */ 194 protected void onKeyInput(final Key key, final int x, final int y) { 195 final int code = key.getCode(); 196 if (code == Constants.CODE_OUTPUT_TEXT) { 197 mListener.onTextInput(mCurrentKey.getOutputText()); 198 } else if (code != Constants.CODE_UNSPECIFIED) { 199 if (getKeyboard().hasProximityCharsCorrection(code)) { 200 mListener.onCodeInput(code, x, y, false /* isKeyRepeat */); 201 } else { 202 mListener.onCodeInput(code, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, 203 false /* isKeyRepeat */); 204 } 205 } 206 } 207 208 private Key detectKey(int x, int y) { 209 final Key oldKey = mCurrentKey; 210 final Key newKey = mKeyDetector.detectHitKey(x, y); 211 if (newKey == oldKey) { 212 return newKey; 213 } 214 // A new key is detected. 215 if (oldKey != null) { 216 updateReleaseKeyGraphics(oldKey); 217 invalidateKey(oldKey); 218 } 219 if (newKey != null) { 220 updatePressKeyGraphics(newKey); 221 invalidateKey(newKey); 222 } 223 return newKey; 224 } 225 226 private void updateReleaseKeyGraphics(final Key key) { 227 key.onReleased(); 228 invalidateKey(key); 229 } 230 231 private void updatePressKeyGraphics(final Key key) { 232 key.onPressed(); 233 invalidateKey(key); 234 } 235 236 @Override 237 public void dismissMoreKeysPanel() { 238 if (!isShowingInParent()) { 239 return; 240 } 241 final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; 242 if (accessibilityDelegate != null) { 243 accessibilityDelegate.onDismissMoreKeysKeyboard(); 244 } 245 mController.onDismissMoreKeysPanel(); 246 } 247 248 @Override 249 public int translateX(final int x) { 250 return x - mOriginX; 251 } 252 253 @Override 254 public int translateY(final int y) { 255 return y - mOriginY; 256 } 257 258 @Override 259 public boolean onTouchEvent(final MotionEvent me) { 260 final int action = me.getActionMasked(); 261 final long eventTime = me.getEventTime(); 262 final int index = me.getActionIndex(); 263 final int x = (int)me.getX(index); 264 final int y = (int)me.getY(index); 265 final int pointerId = me.getPointerId(index); 266 switch (action) { 267 case MotionEvent.ACTION_DOWN: 268 case MotionEvent.ACTION_POINTER_DOWN: 269 onDownEvent(x, y, pointerId, eventTime); 270 break; 271 case MotionEvent.ACTION_UP: 272 case MotionEvent.ACTION_POINTER_UP: 273 onUpEvent(x, y, pointerId, eventTime); 274 break; 275 case MotionEvent.ACTION_MOVE: 276 onMoveEvent(x, y, pointerId, eventTime); 277 break; 278 } 279 return true; 280 } 281 282 /** 283 * {@inheritDoc} 284 */ 285 @Override 286 public boolean onHoverEvent(final MotionEvent event) { 287 final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; 288 if (accessibilityDelegate != null) { 289 return accessibilityDelegate.onHoverEvent(event); 290 } 291 return super.onHoverEvent(event); 292 } 293 294 private View getContainerView() { 295 return (View)getParent(); 296 } 297 298 @Override 299 public void showInParent(final ViewGroup parentView) { 300 removeFromParent(); 301 parentView.addView(getContainerView()); 302 } 303 304 @Override 305 public void removeFromParent() { 306 final View containerView = getContainerView(); 307 final ViewGroup currentParent = (ViewGroup)containerView.getParent(); 308 if (currentParent != null) { 309 currentParent.removeView(containerView); 310 } 311 } 312 313 @Override 314 public boolean isShowingInParent() { 315 return (getContainerView().getParent() != null); 316 } 317} 318