diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java | 572 |
1 files changed, 213 insertions, 359 deletions
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java index 665c641c2..4007c2b55 100644 --- a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java +++ b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java @@ -21,11 +21,11 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Paint.Align; import android.graphics.PorterDuff; import android.graphics.Rect; -import android.graphics.Typeface; -import android.graphics.Paint.Align; import android.graphics.Region.Op; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.inputmethodservice.Keyboard; import android.inputmethodservice.Keyboard.Key; @@ -43,7 +43,6 @@ import android.view.ViewGroup.LayoutParams; import android.widget.PopupWindow; import android.widget.TextView; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -100,8 +99,14 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener * keys. These codes are useful to correct for * accidental presses of a key adjacent to the intended * key. + * @param x + * x-coordinate pixel of touched event. If onKey is not called by onTouchEvent, + * the value should be NOT_A_TOUCH_COORDINATE. + * @param y + * y-coordinate pixel of touched event. If onKey is not called by onTouchEvent, + * the value should be NOT_A_TOUCH_COORDINATE. */ - void onKey(int primaryCode, int[] keyCodes); + void onKey(int primaryCode, int[] keyCodes, int x, int y); /** * Sends a sequence of characters to the listener. @@ -134,8 +139,10 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener void swipeUp(); } + public static final int NOT_A_TOUCH_COORDINATE = -1; + private static final boolean DEBUG = false; - private static final int NOT_A_KEY = -1; + static final int NOT_A_KEY = -1; private static final int[] KEY_DELETE = { Keyboard.KEYCODE_DELETE }; private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable }; @@ -176,7 +183,7 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener private static final int DEBOUNCE_TIME = 70; private int mVerticalCorrection; - private int mProximityThreshold; + private ProximityKeyDetector mProximityKeyDetector = new ProximityKeyDetector(); private boolean mPreviewCentered = false; private boolean mShowPreview = true; @@ -185,13 +192,10 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener private int mPopupPreviewY; private int mWindowY; - private boolean mProximityCorrectOn; - private Paint mPaint; private Rect mPadding; private int mCurrentKey = NOT_A_KEY; - private int mDownKey = NOT_A_KEY; private int mStartX; private int mStartY; @@ -200,20 +204,18 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener private GestureDetector mGestureDetector; private int mPopupX; private int mPopupY; - private int mRepeatKeyIndex = NOT_A_KEY; private int mPopupLayout; private boolean mAbortKey; private Key mInvalidatedKey; private Rect mClipRegion = new Rect(0, 0, 0, 0); - private boolean mPossiblePoly; private SwipeTracker mSwipeTracker = new SwipeTracker(); private int mSwipeThreshold; private boolean mDisambiguateSwipe; // Variables for dealing with multiple pointers private int mOldPointerCount = 1; - private float mOldPointerX; - private float mOldPointerY; + private int mOldPointerX; + private int mOldPointerY; private Drawable mKeyBackground; @@ -221,9 +223,6 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener private static final int REPEAT_START_DELAY = 400; private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); - private static int MAX_NEARBY_KEYS = 12; - private int[] mDistances = new int[MAX_NEARBY_KEYS]; - // For multi-tap private int mLastSentIndex; private int mTapCount; @@ -251,6 +250,8 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener private static final int MSG_REPEAT_KEY = 3; private static final int MSG_LOGPRESS_KEY = 4; + private boolean mInKeyRepeat; + @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -261,12 +262,11 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener mPreviewText.setVisibility(INVISIBLE); break; case MSG_REPEAT_KEY: - if (repeatKey()) { - startKeyRepeatTimer(REPEAT_INTERVAL); - } + repeatKey(msg.arg1); + startKeyRepeatTimer(REPEAT_INTERVAL, msg.arg1); break; case MSG_LOGPRESS_KEY: - openPopupIfRequired((MotionEvent) msg.obj); + openPopupIfRequired(msg.arg1); break; } } @@ -288,34 +288,38 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener removeMessages(MSG_DISMISS_PREVIEW); } - public void startKeyRepeatTimer(long delay) { - sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY), delay); + public void startKeyRepeatTimer(long delay, int keyIndex) { + mInKeyRepeat = true; + sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, keyIndex, 0), delay); } - public void startLongPressTimer(MotionEvent me, long delay) { - sendMessageDelayed(obtainMessage(MSG_LOGPRESS_KEY, me), delay); + public void cancelKeyRepeatTimer() { + mInKeyRepeat = false; + removeMessages(MSG_REPEAT_KEY); } - public void cancelLongPressTimer() { - removeMessages(MSG_LOGPRESS_KEY); + public boolean isInKeyRepeat() { + return mInKeyRepeat; } - public void cancelKeyTimers() { - removeMessages(MSG_REPEAT_KEY); + public void startLongPressTimer(int keyIndex, long delay) { removeMessages(MSG_LOGPRESS_KEY); + sendMessageDelayed(obtainMessage(MSG_LOGPRESS_KEY, keyIndex, 0), delay); } - public void cancelKeyTimersAndPopupPreview() { - removeMessages(MSG_REPEAT_KEY); + public void cancelLongPressTimer() { removeMessages(MSG_LOGPRESS_KEY); - removeMessages(MSG_POPUP_PREVIEW); + } + + public void cancelKeyTimers() { + cancelKeyRepeatTimer(); + cancelLongPressTimer(); } public void cancelAllMessages() { - removeMessages(MSG_REPEAT_KEY); - removeMessages(MSG_LOGPRESS_KEY); - removeMessages(MSG_POPUP_PREVIEW); - removeMessages(MSG_DISMISS_PREVIEW); + cancelKeyTimers(); + cancelPopupPreview(); + cancelDismissPreview(); } }; @@ -540,12 +544,11 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener } private void initGestureDetector() { - mGestureDetector = new GestureDetector( - getContext(), new GestureDetector.SimpleOnGestureListener() { + GestureDetector.SimpleOnGestureListener listener = + new GestureDetector.SimpleOnGestureListener() { @Override public boolean onFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { - if (mPossiblePoly) return false; final float absX = Math.abs(velocityX); final float absY = Math.abs(velocityY); float deltaX = me2.getX() - me1.getX(); @@ -555,44 +558,33 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener mSwipeTracker.computeCurrentVelocity(1000); final float endingVelocityX = mSwipeTracker.getXVelocity(); final float endingVelocityY = mSwipeTracker.getYVelocity(); - boolean sendDownKey = false; if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) { - if (mDisambiguateSwipe && endingVelocityX < velocityX / 4) { - sendDownKey = true; - } else { + if (mDisambiguateSwipe && endingVelocityX >= velocityX / 4) { swipeRight(); return true; } } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) { - if (mDisambiguateSwipe && endingVelocityX > velocityX / 4) { - sendDownKey = true; - } else { + if (mDisambiguateSwipe && endingVelocityX <= velocityX / 4) { swipeLeft(); return true; } } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) { - if (mDisambiguateSwipe && endingVelocityY > velocityY / 4) { - sendDownKey = true; - } else { + if (mDisambiguateSwipe && endingVelocityY <= velocityY / 4) { swipeUp(); return true; } } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) { - if (mDisambiguateSwipe && endingVelocityY < velocityY / 4) { - sendDownKey = true; - } else { + if (mDisambiguateSwipe && endingVelocityY >= velocityY / 4) { swipeDown(); return true; } } - - if (sendDownKey) { - detectAndSendKey(mDownKey, mStartX, mStartY, me1.getEventTime()); - } return false; } - }); + }; + final boolean ignoreMultitouch = true; + mGestureDetector = new GestureDetector(getContext(), listener, null, ignoreMultitouch); mGestureDetector.setIsLongpressEnabled(false); } @@ -620,10 +612,13 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener showPreview(NOT_A_KEY); } // Remove any pending messages, except dismissing preview - mHandler.cancelKeyTimersAndPopupPreview(); + mHandler.cancelKeyTimers(); + mHandler.cancelPopupPreview(); mKeyboard = keyboard; + LatinImeLogger.onSetKeyboard(mKeyboard); List<Key> keys = mKeyboard.getKeys(); mKeys = keys.toArray(new Key[keys.size()]); + mProximityKeyDetector.setKeyboard(keyboard, mKeys); requestLayout(); // Hint to reallocate the buffer if the size changed mKeyboardChanged = true; @@ -718,14 +713,14 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener * @param enabled whether or not the proximity correction is enabled */ public void setProximityCorrectionEnabled(boolean enabled) { - mProximityCorrectOn = enabled; + mProximityKeyDetector.setProximityCorrectionEnabled(enabled); } /** * Returns true if proximity correction is enabled. */ public boolean isProximityCorrectionEnabled() { - return mProximityCorrectOn; + return mProximityKeyDetector.isProximityCorrectionEnabled(); } /** @@ -777,8 +772,7 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener dimensionSum += Math.min(key.width, key.height) + key.gap; } if (dimensionSum < 0 || length == 0) return; - mProximityThreshold = (int) (dimensionSum * 1.4f / length); - mProximityThreshold *= mProximityThreshold; // Square it + mProximityKeyDetector.setProximityThreshold((int) (dimensionSum * 1.4f / length)); final float hysteresisPixel = getContext().getResources() .getDimension(R.dimen.key_debounce_hysteresis_distance); @@ -919,54 +913,6 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener mDirtyRect.setEmpty(); } - private int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys) { - final Key[] keys = mKeys; - int primaryIndex = NOT_A_KEY; - int closestKey = NOT_A_KEY; - int closestKeyDist = mProximityThreshold + 1; - Arrays.fill(mDistances, Integer.MAX_VALUE); - int [] nearestKeyIndices = mKeyboard.getNearestKeys(x, y); - final int keyCount = nearestKeyIndices.length; - for (int i = 0; i < keyCount; i++) { - final Key key = keys[nearestKeyIndices[i]]; - int dist = 0; - boolean isInside = key.isInside(x,y); - if (isInside) { - primaryIndex = nearestKeyIndices[i]; - } - - if (((mProximityCorrectOn - && (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold) - || isInside) - && key.codes[0] > 32) { - // Find insertion point - final int nCodes = key.codes.length; - if (dist < closestKeyDist) { - closestKeyDist = dist; - closestKey = nearestKeyIndices[i]; - } - - if (allKeys == null) continue; - - for (int j = 0; j < mDistances.length; j++) { - if (mDistances[j] > dist) { - // Make space for nCodes codes - System.arraycopy(mDistances, j, mDistances, j + nCodes, - mDistances.length - j - nCodes); - System.arraycopy(allKeys, j, allKeys, j + nCodes, - allKeys.length - j - nCodes); - System.arraycopy(key.codes, 0, allKeys, j, nCodes); - Arrays.fill(mDistances, j, j + nCodes, dist); - break; - } - } - } - } - if (primaryIndex == NOT_A_KEY) { - primaryIndex = closestKey; - } - return primaryIndex; - } private void detectAndSendKey(int index, int x, int y, long eventTime) { if (index != NOT_A_KEY && index < mKeys.length) { @@ -977,13 +923,12 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener } else { int code = key.codes[0]; //TextEntryState.keyPressedAt(key, x, y); - int[] codes = new int[MAX_NEARBY_KEYS]; - Arrays.fill(codes, NOT_A_KEY); - getKeyIndexAndNearbyCodes(x, y, codes); + int[] codes = mProximityKeyDetector.newCodeArray(); + mProximityKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes); // Multi-tap if (mInMultiTap) { if (mTapCount != -1) { - mKeyboardActionListener.onKey(Keyboard.KEYCODE_DELETE, KEY_DELETE); + mKeyboardActionListener.onKey(Keyboard.KEYCODE_DELETE, KEY_DELETE, x, y); } else { mTapCount = 0; } @@ -998,7 +943,7 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener codes[1] = codes[0]; codes[0] = code; } - mKeyboardActionListener.onKey(code, codes); + mKeyboardActionListener.onKey(code, codes, x, y); mKeyboardActionListener.onRelease(code); } mLastSentIndex = index; @@ -1166,16 +1111,16 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop()); } - private boolean openPopupIfRequired(MotionEvent me) { + private boolean openPopupIfRequired(int keyIndex) { // Check if we have a popup layout specified first. if (mPopupLayout == 0) { return false; } - if (mCurrentKey < 0 || mCurrentKey >= mKeys.length) { + if (keyIndex < 0 || keyIndex >= mKeys.length) { return false; } - Key popupKey = mKeys[mCurrentKey]; + Key popupKey = mKeys[keyIndex]; boolean result = onLongPress(popupKey); if (result) { mAbortKey = true; @@ -1206,8 +1151,8 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener R.id.closeButton); if (closeButton != null) closeButton.setOnClickListener(this); mMiniKeyboard.setOnKeyboardActionListener(new OnKeyboardActionListener() { - public void onKey(int primaryCode, int[] keyCodes) { - mKeyboardActionListener.onKey(primaryCode, keyCodes); + public void onKey(int primaryCode, int[] keyCodes, int x, int y) { + mKeyboardActionListener.onKey(primaryCode, keyCodes, x, y); dismissPopupKeyboard(); } @@ -1270,58 +1215,28 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener return false; } + private int getTouchX(float x) { + return (int)x - getPaddingLeft(); + } + + private int getTouchY(float y) { + return (int)y + mVerticalCorrection - getPaddingTop(); + } + @Override public boolean onTouchEvent(MotionEvent me) { // Convert multi-pointer up/down events to single up/down events to // deal with the typical multi-pointer behavior of two-thumb typing final int pointerCount = me.getPointerCount(); final int action = me.getAction(); - boolean result = false; - final long now = me.getEventTime(); + final long eventTime = me.getEventTime(); - if (pointerCount != mOldPointerCount) { - if (pointerCount == 1) { - // Send a down event for the latest pointer - MotionEvent down = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, - me.getX(), me.getY(), me.getMetaState()); - result = onModifiedTouchEvent(down, false); - down.recycle(); - // If it's an up action, then deliver the up as well. - if (action == MotionEvent.ACTION_UP) { - result = onModifiedTouchEvent(me, true); - } - } else { - // Send an up event for the last pointer - MotionEvent up = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, - mOldPointerX, mOldPointerY, me.getMetaState()); - result = onModifiedTouchEvent(up, true); - up.recycle(); - } - } else { - if (pointerCount == 1) { - result = onModifiedTouchEvent(me, false); - mOldPointerX = me.getX(); - mOldPointerY = me.getY(); - } else { - // Don't do anything when 2 pointers are down and moving. - result = true; - } + if (pointerCount > 1 && mOldPointerCount > 1) { + // Don't do anything when 2 or more pointers are down and moving. + return true; } - mOldPointerCount = pointerCount; - - return result; - } - - private boolean onModifiedTouchEvent(MotionEvent me, boolean possiblePoly) { - int touchX = (int) me.getX() - getPaddingLeft(); - int touchY = (int) me.getY() + mVerticalCorrection - getPaddingTop(); - final int action = me.getAction(); - final long eventTime = me.getEventTime(); - int keyIndex = getKeyIndexAndNearbyCodes(touchX, touchY, null); - mPossiblePoly = possiblePoly; // Track the last few movements to look for spurious swipes. - if (action == MotionEvent.ACTION_DOWN) mSwipeTracker.clear(); mSwipeTracker.addMovement(me); // Ignore all motion events until a DOWN. @@ -1342,107 +1257,156 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener return true; } + if (mHandler.isInKeyRepeat()) { + // It'll be canceled if 2 or more keys are in action. Otherwise it will keep being in + // the key repeating mode while the key is being pressed. + if (pointerCount > 1) { + mHandler.cancelKeyRepeatTimer(); + } else if (action == MotionEvent.ACTION_MOVE) { + return true; + } + // Up event will pass through. + } + + int touchX = getTouchX(me.getX()); + int touchY = getTouchY(me.getY()); + if (pointerCount != mOldPointerCount) { + if (pointerCount == 1) { + // Send a down event for the latest pointer + onDownEvent(touchX, touchY, eventTime); + // If it's an up action, then deliver the up as well. + if (action == MotionEvent.ACTION_UP) { + onUpEvent(touchX, touchY, eventTime); + } + } else { + // Send an up event for the last pointer + onUpEvent(mOldPointerX, mOldPointerY, eventTime); + } + mOldPointerCount = pointerCount; + return true; + } else { + if (pointerCount == 1) { + onModifiedTouchEvent(action, touchX, touchY, eventTime); + mOldPointerX = touchX; + mOldPointerY = touchY; + return true; + } + } + + return false; + } + + private void onModifiedTouchEvent(int action, int touchX, int touchY, long eventTime) { switch (action) { case MotionEvent.ACTION_DOWN: - mAbortKey = false; - mCurrentKey = keyIndex; - mDownKey = keyIndex; - mStartX = touchX; - mStartY = touchY; - mDebouncer.startMoveDebouncing(touchX, touchY); - mDebouncer.startTimeDebouncing(eventTime); - checkMultiTap(eventTime, keyIndex); - mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ? - mKeys[keyIndex].codes[0] : 0); - if (mCurrentKey >= 0 && mKeys[mCurrentKey].repeatable) { - mRepeatKeyIndex = mCurrentKey; - mHandler.startKeyRepeatTimer(REPEAT_START_DELAY); - repeatKey(); - // Delivering the key could have caused an abort - if (mAbortKey) { - mRepeatKeyIndex = NOT_A_KEY; - break; - } - } - if (mCurrentKey != NOT_A_KEY) { - mHandler.startLongPressTimer(me, LONGPRESS_TIMEOUT); - } - showPreview(keyIndex); + onDownEvent(touchX, touchY, eventTime); break; - case MotionEvent.ACTION_MOVE: - boolean continueLongPress = false; - if (keyIndex != NOT_A_KEY) { - if (mCurrentKey == NOT_A_KEY) { - mCurrentKey = keyIndex; - mDebouncer.updateTimeDebouncing(eventTime); - } else if (mDebouncer.isMinorMoveBounce(touchX, touchY, keyIndex, - mCurrentKey)) { - mDebouncer.updateTimeDebouncing(eventTime); - continueLongPress = true; - } else if (mRepeatKeyIndex == NOT_A_KEY) { - resetMultiTap(); - mDebouncer.resetTimeDebouncing(eventTime, mCurrentKey); - mDebouncer.resetMoveDebouncing(); - mCurrentKey = keyIndex; - } - } - if (!continueLongPress) { - // Cancel old longpress - mHandler.cancelLongPressTimer(); - // Start new longpress if key has changed - if (keyIndex != NOT_A_KEY) { - mHandler.startLongPressTimer(me, LONGPRESS_TIMEOUT); - } - } - /* - * While time debouncing is in effect, mCurrentKey holds the new key and mDebouncer - * holds the last key. At ACTION_UP event if time debouncing will be in effect - * eventually, the last key should be sent as the result. In such case mCurrentKey - * should not be showed as popup preview. - */ - showPreview(mDebouncer.isMinorTimeBounce() ? mDebouncer.getLastKey() : mCurrentKey); + onMoveEvent(touchX, touchY, eventTime); break; - case MotionEvent.ACTION_UP: - mHandler.cancelKeyTimersAndPopupPreview(); - if (mDebouncer.isMinorMoveBounce(touchX, touchY, keyIndex, mCurrentKey)) { - mDebouncer.updateTimeDebouncing(eventTime); - } else { - resetMultiTap(); - mDebouncer.resetTimeDebouncing(eventTime, mCurrentKey); - mCurrentKey = keyIndex; - } - if (mDebouncer.isMinorTimeBounce()) { - mCurrentKey = mDebouncer.getLastKey(); - touchX = mDebouncer.getLastCodeX(); - touchY = mDebouncer.getLastCodeY(); - } - showPreview(NOT_A_KEY); - // If we're not on a repeating key (which sends on a DOWN event) - if (mRepeatKeyIndex == NOT_A_KEY && !mMiniKeyboardOnScreen && !mAbortKey) { - detectAndSendKey(mCurrentKey, touchX, touchY, eventTime); - } - invalidateKey(keyIndex); - mRepeatKeyIndex = NOT_A_KEY; + onUpEvent(touchX, touchY, eventTime); break; - case MotionEvent.ACTION_CANCEL: - mHandler.cancelKeyTimersAndPopupPreview(); - dismissPopupKeyboard(); - mAbortKey = true; - showPreview(NOT_A_KEY); - invalidateKey(mCurrentKey); + onCancelEvent(touchX, touchY, eventTime); break; } + } + + private void onDownEvent(int touchX, int touchY, long eventTime) { + int keyIndex = mProximityKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null); + mAbortKey = false; + mCurrentKey = keyIndex; + mStartX = touchX; + mStartY = touchY; + mDebouncer.startMoveDebouncing(touchX, touchY); + mDebouncer.startTimeDebouncing(eventTime); + checkMultiTap(eventTime, keyIndex); + mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ? mKeys[keyIndex].codes[0] : 0); + if (keyIndex >= 0 && mKeys[keyIndex].repeatable) { + repeatKey(keyIndex); + mHandler.startKeyRepeatTimer(REPEAT_START_DELAY, keyIndex); + // Delivering the key could have caused an abort + if (mAbortKey) { + mHandler.cancelKeyRepeatTimer(); + return; + } + } + if (keyIndex != NOT_A_KEY) { + mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT); + } + showPreview(keyIndex); mDebouncer.updateMoveDebouncing(touchX, touchY); - return true; } - private boolean repeatKey() { - Key key = mKeys[mRepeatKeyIndex]; - detectAndSendKey(mCurrentKey, key.x, key.y, mLastTapTime); - return true; + private void onMoveEvent(int touchX, int touchY, long eventTime) { + int keyIndex = mProximityKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null); + if (keyIndex != NOT_A_KEY) { + if (mCurrentKey == NOT_A_KEY) { + mDebouncer.updateTimeDebouncing(eventTime); + mCurrentKey = keyIndex; + mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT); + } else if (mDebouncer.isMinorMoveBounce(touchX, touchY, keyIndex, mCurrentKey)) { + mDebouncer.updateTimeDebouncing(eventTime); + } else { + resetMultiTap(); + mDebouncer.resetTimeDebouncing(eventTime, mCurrentKey); + mDebouncer.resetMoveDebouncing(); + mCurrentKey = keyIndex; + mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT); + } + } else { + mHandler.cancelLongPressTimer(); + } + /* + * While time debouncing is in effect, mCurrentKey holds the new key and mDebouncer + * holds the last key. At ACTION_UP event if time debouncing will be in effect + * eventually, the last key should be sent as the result. In such case mCurrentKey + * should not be showed as popup preview. + */ + showPreview(mDebouncer.isMinorTimeBounce() ? mDebouncer.getLastKey() : mCurrentKey); + mDebouncer.updateMoveDebouncing(touchX, touchY); + } + + private void onUpEvent(int touchX, int touchY, long eventTime) { + int keyIndex = mProximityKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null); + boolean wasInKeyRepeat = mHandler.isInKeyRepeat(); + mHandler.cancelKeyTimers(); + mHandler.cancelPopupPreview(); + if (mDebouncer.isMinorMoveBounce(touchX, touchY, keyIndex, mCurrentKey)) { + mDebouncer.updateTimeDebouncing(eventTime); + } else { + resetMultiTap(); + mDebouncer.resetTimeDebouncing(eventTime, mCurrentKey); + mCurrentKey = keyIndex; + } + if (mDebouncer.isMinorTimeBounce()) { + mCurrentKey = mDebouncer.getLastKey(); + touchX = mDebouncer.getLastCodeX(); + touchY = mDebouncer.getLastCodeY(); + } + showPreview(NOT_A_KEY); + // If we're not on a repeating key (which sends on a DOWN event) + if (!wasInKeyRepeat && !mMiniKeyboardOnScreen && !mAbortKey) { + detectAndSendKey(mCurrentKey, touchX, touchY, eventTime); + } + invalidateKey(keyIndex); + } + + private void onCancelEvent(int touchX, int touchY, long eventTime) { + mHandler.cancelKeyTimers(); + mHandler.cancelPopupPreview(); + dismissPopupKeyboard(); + mAbortKey = true; + showPreview(NOT_A_KEY); + invalidateKey(mCurrentKey); + } + + private void repeatKey(int keyIndex) { + Key key = mKeys[keyIndex]; + // While key is repeating, because there is no need to handle multi-tap key, we can pass + // -1 as eventTime argument. + detectAndSendKey(keyIndex, key.x, key.y, -1); } protected void swipeRight() { @@ -1520,114 +1484,4 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener resetMultiTap(); } } - - private static class SwipeTracker { - - static final int NUM_PAST = 4; - static final int LONGEST_PAST_TIME = 200; - - final float mPastX[] = new float[NUM_PAST]; - final float mPastY[] = new float[NUM_PAST]; - final long mPastTime[] = new long[NUM_PAST]; - - float mYVelocity; - float mXVelocity; - - public void clear() { - mPastTime[0] = 0; - } - - public void addMovement(MotionEvent ev) { - long time = ev.getEventTime(); - final int N = ev.getHistorySize(); - for (int i=0; i<N; i++) { - addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i), - ev.getHistoricalEventTime(i)); - } - addPoint(ev.getX(), ev.getY(), time); - } - - private void addPoint(float x, float y, long time) { - int drop = -1; - int i; - final long[] pastTime = mPastTime; - for (i=0; i<NUM_PAST; i++) { - if (pastTime[i] == 0) { - break; - } else if (pastTime[i] < time-LONGEST_PAST_TIME) { - drop = i; - } - } - if (i == NUM_PAST && drop < 0) { - drop = 0; - } - if (drop == i) drop--; - final float[] pastX = mPastX; - final float[] pastY = mPastY; - if (drop >= 0) { - final int start = drop+1; - final int count = NUM_PAST-drop-1; - System.arraycopy(pastX, start, pastX, 0, count); - System.arraycopy(pastY, start, pastY, 0, count); - System.arraycopy(pastTime, start, pastTime, 0, count); - i -= (drop+1); - } - pastX[i] = x; - pastY[i] = y; - pastTime[i] = time; - i++; - if (i < NUM_PAST) { - pastTime[i] = 0; - } - } - - public void computeCurrentVelocity(int units) { - computeCurrentVelocity(units, Float.MAX_VALUE); - } - - public void computeCurrentVelocity(int units, float maxVelocity) { - final float[] pastX = mPastX; - final float[] pastY = mPastY; - final long[] pastTime = mPastTime; - - final float oldestX = pastX[0]; - final float oldestY = pastY[0]; - final long oldestTime = pastTime[0]; - float accumX = 0; - float accumY = 0; - int N=0; - while (N < NUM_PAST) { - if (pastTime[N] == 0) { - break; - } - N++; - } - - for (int i=1; i < N; i++) { - final int dur = (int)(pastTime[i] - oldestTime); - if (dur == 0) continue; - float dist = pastX[i] - oldestX; - float vel = (dist/dur) * units; // pixels/frame. - if (accumX == 0) accumX = vel; - else accumX = (accumX + vel) * .5f; - - dist = pastY[i] - oldestY; - vel = (dist/dur) * units; // pixels/frame. - if (accumY == 0) accumY = vel; - else accumY = (accumY + vel) * .5f; - } - mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity) - : Math.min(accumX, maxVelocity); - mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity) - : Math.min(accumY, maxVelocity); - } - - public float getXVelocity() { - return mXVelocity; - } - - public float getYVelocity() { - return mYVelocity; - } - } } |