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.Resources; 21 import android.util.AttributeSet; 22 import android.view.MotionEvent; 23 import android.view.View; 24 25 import com.android.inputmethod.latin.Constants; 26 import com.android.inputmethod.latin.R; 27 import com.android.inputmethod.latin.utils.CoordinateUtils; 28 29 /** 30 * A view that renders a virtual {@link MoreKeysKeyboard}. It handles rendering of keys and 31 * detecting key presses and touch movements. 32 */ 33 public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel { 34 private final int[] mCoordinates = CoordinateUtils.newInstance(); 35 36 protected final KeyDetector mKeyDetector; 37 private Controller mController = EMPTY_CONTROLLER; 38 protected KeyboardActionListener mListener; 39 private int mOriginX; 40 private int mOriginY; 41 private Key mCurrentKey; 42 43 private int mActivePointerId; 44 45 public MoreKeysKeyboardView(final Context context, final AttributeSet attrs) { 46 this(context, attrs, R.attr.moreKeysKeyboardViewStyle); 47 } 48 49 public MoreKeysKeyboardView(final Context context, final AttributeSet attrs, 50 final int defStyle) { 51 super(context, attrs, defStyle); 52 53 final Resources res = context.getResources(); 54 mKeyDetector = new MoreKeysDetector( 55 res.getDimension(R.dimen.more_keys_keyboard_slide_allowance)); 56 } 57 58 @Override 59 protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { 60 final Keyboard keyboard = getKeyboard(); 61 if (keyboard != null) { 62 final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight(); 63 final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom(); 64 setMeasuredDimension(width, height); 65 } else { 66 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 67 } 68 } 69 70 @Override 71 public void setKeyboard(final Keyboard keyboard) { 72 super.setKeyboard(keyboard); 73 mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), 74 -getPaddingTop() + getVerticalCorrection()); 75 } 76 77 @Override 78 public void showMoreKeysPanel(final View parentView, final Controller controller, 79 final int pointX, final int pointY, final KeyboardActionListener listener) { 80 mController = controller; 81 mListener = listener; 82 final View container = getContainerView(); 83 // The coordinates of panel's left-top corner in parentView's coordinate system. 84 final int x = pointX - getDefaultCoordX() - container.getPaddingLeft(); 85 final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom(); 86 87 parentView.getLocationInWindow(mCoordinates); 88 // Ensure the horizontal position of the panel does not extend past the screen edges. 89 final int maxX = parentView.getMeasuredWidth() - container.getMeasuredWidth(); 90 final int panelX = Math.max(0, Math.min(maxX, x)) + CoordinateUtils.x(mCoordinates); 91 final int panelY = y + CoordinateUtils.y(mCoordinates); 92 container.setX(panelX); 93 container.setY(panelY); 94 95 mOriginX = x + container.getPaddingLeft(); 96 mOriginY = y + container.getPaddingTop(); 97 controller.onShowMoreKeysPanel(this); 98 } 99 100 /** 101 * Returns the default x coordinate for showing this panel. 102 */ 103 protected int getDefaultCoordX() { 104 return ((MoreKeysKeyboard)getKeyboard()).getDefaultCoordX(); 105 } 106 107 @Override 108 public void onDownEvent(final int x, final int y, final int pointerId, final long eventTime) { 109 mActivePointerId = pointerId; 110 onMoveKeyInternal(x, y, pointerId); 111 } 112 113 @Override 114 public void onMoveEvent(int x, int y, final int pointerId, long eventTime) { 115 if (mActivePointerId != pointerId) { 116 return; 117 } 118 final boolean hasOldKey = (mCurrentKey != null); 119 onMoveKeyInternal(x, y, pointerId); 120 if (hasOldKey && mCurrentKey == null) { 121 // If the pointer has moved too far away from any target then cancel the panel. 122 mController.onCancelMoreKeysPanel(this); 123 } 124 } 125 126 @Override 127 public void onUpEvent(final int x, final int y, final int pointerId, final long eventTime) { 128 if (mCurrentKey != null && mActivePointerId == pointerId) { 129 updateReleaseKeyGraphics(mCurrentKey); 130 onCodeInput(mCurrentKey.getCode(), x, y); 131 mCurrentKey = null; 132 } 133 } 134 135 /** 136 * Performs the specific action for this panel when the user presses a key on the panel. 137 */ 138 protected void onCodeInput(final int code, final int x, final int y) { 139 if (code == Constants.CODE_OUTPUT_TEXT) { 140 mListener.onTextInput(mCurrentKey.getOutputText()); 141 } else if (code != Constants.CODE_UNSPECIFIED) { 142 mListener.onCodeInput(code, x, y); 143 } 144 } 145 146 private void onMoveKeyInternal(int x, int y, int pointerId) { 147 if (mActivePointerId != pointerId) { 148 // Ignore old pointers when newer pointer is active. 149 return; 150 } 151 final Key oldKey = mCurrentKey; 152 final Key newKey = mKeyDetector.detectHitKey(x, y); 153 if (newKey != oldKey) { 154 mCurrentKey = newKey; 155 invalidateKey(mCurrentKey); 156 if (oldKey != null) { 157 updateReleaseKeyGraphics(oldKey); 158 } 159 if (newKey != null) { 160 updatePressKeyGraphics(newKey); 161 } 162 } 163 } 164 165 private void updateReleaseKeyGraphics(final Key key) { 166 key.onReleased(); 167 invalidateKey(key); 168 } 169 170 private void updatePressKeyGraphics(final Key key) { 171 key.onPressed(); 172 invalidateKey(key); 173 } 174 175 @Override 176 public void dismissMoreKeysPanel() { 177 if (!isShowingInParent()) { 178 return; 179 } 180 mController.onDismissMoreKeysPanel(this); 181 } 182 183 @Override 184 public int translateX(final int x) { 185 return x - mOriginX; 186 } 187 188 @Override 189 public int translateY(final int y) { 190 return y - mOriginY; 191 } 192 193 @Override 194 public boolean onTouchEvent(final MotionEvent me) { 195 final int action = me.getActionMasked(); 196 final long eventTime = me.getEventTime(); 197 final int index = me.getActionIndex(); 198 final int x = (int)me.getX(index); 199 final int y = (int)me.getY(index); 200 final int pointerId = me.getPointerId(index); 201 switch (action) { 202 case MotionEvent.ACTION_DOWN: 203 case MotionEvent.ACTION_POINTER_DOWN: 204 onDownEvent(x, y, pointerId, eventTime); 205 break; 206 case MotionEvent.ACTION_UP: 207 case MotionEvent.ACTION_POINTER_UP: 208 onUpEvent(x, y, pointerId, eventTime); 209 break; 210 case MotionEvent.ACTION_MOVE: 211 onMoveEvent(x, y, pointerId, eventTime); 212 break; 213 } 214 return true; 215 } 216 217 @Override 218 public View getContainerView() { 219 return (View)getParent(); 220 } 221 222 @Override 223 public boolean isShowingInParent() { 224 return (getContainerView().getParent() != null); 225 } 226 } 227