aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java')
-rw-r--r--java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java572
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;
- }
- }
}