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