Fixed key code and key coordinates when move debounce has been in action

This change refactors a key index and pointer position variables into
a separate static inner class KeyState . This change also disables
time debouncing.

Bug: 3033737
Change-Id: Ie4fc37316c260330d8f0861e0771ea903a99cfce
main
Tadashi G. Takaoka 2010-09-24 20:45:24 +09:00
parent 008e9b3e1a
commit 6e5a398685
1 changed files with 129 additions and 119 deletions

View File

@ -42,7 +42,6 @@ public class PointerTracker {
/* package */ static final int REPEAT_INTERVAL = 50; // ~20 keys per second /* package */ static final int REPEAT_INTERVAL = 50; // ~20 keys per second
private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
private static final int MULTITAP_INTERVAL = 800; // milliseconds private static final int MULTITAP_INTERVAL = 800; // milliseconds
private static final int KEY_DEBOUNCE_TIME = 70;
// Miscellaneous constants // Miscellaneous constants
private static final int NOT_A_KEY = LatinKeyboardBaseView.NOT_A_KEY; private static final int NOT_A_KEY = LatinKeyboardBaseView.NOT_A_KEY;
@ -57,10 +56,7 @@ public class PointerTracker {
private Key[] mKeys; private Key[] mKeys;
private int mKeyHysteresisDistanceSquared = -1; private int mKeyHysteresisDistanceSquared = -1;
private int mCurrentKey = NOT_A_KEY; private final KeyState mKeyState;
private int mStartX;
private int mStartY;
private long mDownTime;
// true if event is already translated to a key action (long press or mini-keyboard) // true if event is already translated to a key action (long press or mini-keyboard)
private boolean mKeyAlreadyProcessed; private boolean mKeyAlreadyProcessed;
@ -68,18 +64,6 @@ public class PointerTracker {
// true if this pointer is repeatable key // true if this pointer is repeatable key
private boolean mIsRepeatableKey; private boolean mIsRepeatableKey;
// for move de-bouncing
private int mLastCodeX;
private int mLastCodeY;
private int mLastX;
private int mLastY;
// for time de-bouncing
private int mLastKey;
private long mLastKeyTime;
private long mLastMoveTime;
private long mCurrentKeyTime;
// For multi-tap // For multi-tap
private int mLastSentIndex; private int mLastSentIndex;
private int mTapCount; private int mTapCount;
@ -90,6 +74,95 @@ public class PointerTracker {
// pressed key // pressed key
private int mPreviousKey = NOT_A_KEY; private int mPreviousKey = NOT_A_KEY;
// This class keeps track of a key index and a position where this pointer is.
private static class KeyState {
private final KeyDetector mKeyDetector;
// The position and time at which first down event occurred.
private int mStartX;
private int mStartY;
private long mDownTime;
// The current key index where this pointer is.
private int mKeyIndex = NOT_A_KEY;
// The position where mKeyIndex was recognized for the first time.
private int mKeyX;
private int mKeyY;
// Last pointer position.
private int mLastX;
private int mLastY;
public KeyState(KeyDetector keyDetecor) {
mKeyDetector = keyDetecor;
}
public int getKeyIndex() {
return mKeyIndex;
}
public int getKeyX() {
return mKeyX;
}
public int getKeyY() {
return mKeyY;
}
public int getStartX() {
return mStartX;
}
public int getStartY() {
return mStartY;
}
public long getDownTime() {
return mDownTime;
}
public int getLastX() {
return mLastX;
}
public int getLastY() {
return mLastY;
}
public int onDownKey(int x, int y, long eventTime) {
mStartX = x;
mStartY = y;
mDownTime = eventTime;
return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
}
private int onMoveKeyInternal(int x, int y) {
mLastX = x;
mLastY = y;
return mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
}
public int onMoveKey(int x, int y) {
return onMoveKeyInternal(x, y);
}
public int onMoveToNewKey(int keyIndex, int x, int y) {
mKeyIndex = keyIndex;
mKeyX = x;
mKeyY = y;
return keyIndex;
}
public int onUpKey(int x, int y) {
return onMoveKeyInternal(x, y);
}
public void onSetKeyboard() {
mKeyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(mKeyX, mKeyY, null);
}
}
public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy, public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy,
boolean hasDistinctMultitouch) { boolean hasDistinctMultitouch) {
if (proxy == null || handler == null || keyDetector == null) if (proxy == null || handler == null || keyDetector == null)
@ -98,6 +171,7 @@ public class PointerTracker {
mProxy = proxy; mProxy = proxy;
mHandler = handler; mHandler = handler;
mKeyDetector = keyDetector; mKeyDetector = keyDetector;
mKeyState = new KeyState(keyDetector);
mHasDistinctMultitouch = hasDistinctMultitouch; mHasDistinctMultitouch = hasDistinctMultitouch;
resetMultiTap(); resetMultiTap();
} }
@ -112,7 +186,7 @@ public class PointerTracker {
mKeys = keys; mKeys = keys;
mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance); mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance);
// Update current key index because keyboard layout has been changed. // Update current key index because keyboard layout has been changed.
mCurrentKey = mKeyDetector.getKeyIndexAndNearbyCodes(mStartX, mStartY, null); mKeyState.onSetKeyboard();
} }
private boolean isValidKeyIndex(int keyIndex) { private boolean isValidKeyIndex(int keyIndex) {
@ -133,7 +207,7 @@ public class PointerTracker {
} }
public boolean isModifier() { public boolean isModifier() {
return isModifierInternal(mCurrentKey); return isModifierInternal(mKeyState.getKeyIndex());
} }
public boolean isOnModifierKey(int x, int y) { public boolean isOnModifierKey(int x, int y) {
@ -190,21 +264,16 @@ public class PointerTracker {
public void onDownEvent(int x, int y, long eventTime) { public void onDownEvent(int x, int y, long eventTime) {
if (DEBUG) if (DEBUG)
debugLog("onDownEvent:", x, y); debugLog("onDownEvent:", x, y);
int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null); int keyIndex = mKeyState.onDownKey(x, y, eventTime);
mCurrentKey = keyIndex;
mStartX = x;
mStartY = y;
mDownTime = eventTime;
mKeyAlreadyProcessed = false; mKeyAlreadyProcessed = false;
mIsRepeatableKey = false; mIsRepeatableKey = false;
startMoveDebouncing(x, y);
startTimeDebouncing(eventTime);
checkMultiTap(eventTime, keyIndex); checkMultiTap(eventTime, keyIndex);
if (mListener != null) { if (mListener != null) {
int primaryCode = isValidKeyIndex(keyIndex) ? mKeys[keyIndex].codes[0] : 0; int primaryCode = isValidKeyIndex(keyIndex) ? mKeys[keyIndex].codes[0] : 0;
mListener.onPress(primaryCode); mListener.onPress(primaryCode);
// This onPress call may have changed keyboard layout and have updated mCurrentKey // This onPress call may have changed keyboard layout and have updated mKeyIndex.
keyIndex = mCurrentKey; // If that's the case, mKeyIndex has been updated in setKeyboard().
keyIndex = mKeyState.getKeyIndex();
} }
if (isValidKeyIndex(keyIndex)) { if (isValidKeyIndex(keyIndex)) {
if (mKeys[keyIndex].repeatable) { if (mKeys[keyIndex].repeatable) {
@ -215,7 +284,6 @@ public class PointerTracker {
mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this); mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this);
} }
showKeyPreviewAndUpdateKey(keyIndex); showKeyPreviewAndUpdateKey(keyIndex);
updateMoveDebouncing(x, y);
} }
public void onMoveEvent(int x, int y, long eventTime) { public void onMoveEvent(int x, int y, long eventTime) {
@ -223,44 +291,28 @@ public class PointerTracker {
debugLog("onMoveEvent:", x, y); debugLog("onMoveEvent:", x, y);
if (mKeyAlreadyProcessed) if (mKeyAlreadyProcessed)
return; return;
int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null); KeyState keyState = mKeyState;
int keyIndex = keyState.onMoveKey(x, y);
if (isValidKeyIndex(keyIndex)) { if (isValidKeyIndex(keyIndex)) {
if (mCurrentKey == NOT_A_KEY) { if (keyState.getKeyIndex() == NOT_A_KEY) {
updateTimeDebouncing(eventTime); keyState.onMoveToNewKey(keyIndex, x, y);
mCurrentKey = keyIndex;
mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this); mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this);
} else if (isMinorMoveBounce(x, y, keyIndex, mCurrentKey)) { } else if (!isMinorMoveBounce(x, y, keyIndex)) {
updateTimeDebouncing(eventTime);
} else {
resetMultiTap(); resetMultiTap();
resetTimeDebouncing(eventTime, mCurrentKey); keyState.onMoveToNewKey(keyIndex, x, y);
resetMoveDebouncing();
mCurrentKey = keyIndex;
mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this); mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this);
} }
} else { } else {
if (mCurrentKey != NOT_A_KEY) { if (keyState.getKeyIndex() != NOT_A_KEY) {
updateTimeDebouncing(eventTime); keyState.onMoveToNewKey(keyIndex, x ,y);
mCurrentKey = keyIndex;
mHandler.cancelLongPressTimer(); mHandler.cancelLongPressTimer();
} else if (isMinorMoveBounce(x, y, keyIndex, mCurrentKey)) { } else if (!isMinorMoveBounce(x, y, keyIndex)) {
updateTimeDebouncing(eventTime);
} else {
resetMultiTap(); resetMultiTap();
resetTimeDebouncing(eventTime, mCurrentKey); keyState.onMoveToNewKey(keyIndex, x ,y);
resetMoveDebouncing();
mCurrentKey = keyIndex;
mHandler.cancelLongPressTimer(); mHandler.cancelLongPressTimer();
} }
} }
/* showKeyPreviewAndUpdateKey(mKeyState.getKeyIndex());
* While time debouncing is in effect, mCurrentKey holds the new key and this tracker
* 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.
*/
showKeyPreviewAndUpdateKey(isMinorTimeBounce() ? mLastKey : mCurrentKey);
updateMoveDebouncing(x, y);
} }
public void onUpEvent(int x, int y, long eventTime) { public void onUpEvent(int x, int y, long eventTime) {
@ -270,23 +322,18 @@ public class PointerTracker {
return; return;
mHandler.cancelKeyTimers(); mHandler.cancelKeyTimers();
mHandler.cancelPopupPreview(); mHandler.cancelPopupPreview();
int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null); int keyIndex = mKeyState.onUpKey(x, y);
if (isMinorMoveBounce(x, y, keyIndex, mCurrentKey)) { if (isMinorMoveBounce(x, y, keyIndex)) {
updateTimeDebouncing(eventTime); // Use previous fixed key index and coordinates.
} else { keyIndex = mKeyState.getKeyIndex();
resetMultiTap(); x = mKeyState.getKeyX();
resetTimeDebouncing(eventTime, mCurrentKey); y = mKeyState.getKeyY();
mCurrentKey = keyIndex;
}
if (isMinorTimeBounce()) {
mCurrentKey = mLastKey;
x = mLastCodeX;
y = mLastCodeY;
} }
showKeyPreviewAndUpdateKey(NOT_A_KEY); showKeyPreviewAndUpdateKey(NOT_A_KEY);
if (!mIsRepeatableKey) { if (!mIsRepeatableKey) {
detectAndSendKey(mCurrentKey, x, y, eventTime); detectAndSendKey(keyIndex, x, y, eventTime);
} }
if (isValidKeyIndex(keyIndex)) if (isValidKeyIndex(keyIndex))
mProxy.invalidateKey(mKeys[keyIndex]); mProxy.invalidateKey(mKeys[keyIndex]);
} }
@ -297,7 +344,7 @@ public class PointerTracker {
mHandler.cancelKeyTimers(); mHandler.cancelKeyTimers();
mHandler.cancelPopupPreview(); mHandler.cancelPopupPreview();
showKeyPreviewAndUpdateKey(NOT_A_KEY); showKeyPreviewAndUpdateKey(NOT_A_KEY);
int keyIndex = mCurrentKey; int keyIndex = mKeyState.getKeyIndex();
if (isValidKeyIndex(keyIndex)) if (isValidKeyIndex(keyIndex))
mProxy.invalidateKey(mKeys[keyIndex]); mProxy.invalidateKey(mKeys[keyIndex]);
} }
@ -312,44 +359,30 @@ public class PointerTracker {
} }
public int getLastX() { public int getLastX() {
return mLastX; return mKeyState.getLastX();
} }
public int getLastY() { public int getLastY() {
return mLastY; return mKeyState.getLastY();
} }
public long getDownTime() { public long getDownTime() {
return mDownTime; return mKeyState.getDownTime();
} }
// These package scope methods are only for debugging purpose. // These package scope methods are only for debugging purpose.
/* package */ int getStartX() { /* package */ int getStartX() {
return mStartX; return mKeyState.getStartX();
} }
/* package */ int getStartY() { /* package */ int getStartY() {
return mStartY; return mKeyState.getStartY();
} }
private void startMoveDebouncing(int x, int y) { private boolean isMinorMoveBounce(int x, int y, int newKey) {
mLastCodeX = x;
mLastCodeY = y;
}
private void updateMoveDebouncing(int x, int y) {
mLastX = x;
mLastY = y;
}
private void resetMoveDebouncing() {
mLastCodeX = mLastX;
mLastCodeY = mLastY;
}
private boolean isMinorMoveBounce(int x, int y, int newKey, int curKey) {
if (mKeys == null || mKeyHysteresisDistanceSquared < 0) if (mKeys == null || mKeyHysteresisDistanceSquared < 0)
throw new IllegalStateException("keyboard and/or hysteresis not set"); throw new IllegalStateException("keyboard and/or hysteresis not set");
int curKey = mKeyState.getKeyIndex();
if (newKey == curKey) { if (newKey == curKey) {
return true; return true;
} else if (isValidKeyIndex(curKey)) { } else if (isValidKeyIndex(curKey)) {
@ -371,30 +404,6 @@ public class PointerTracker {
return dx * dx + dy * dy; return dx * dx + dy * dy;
} }
private void startTimeDebouncing(long eventTime) {
mLastKey = NOT_A_KEY;
mLastKeyTime = 0;
mCurrentKeyTime = 0;
mLastMoveTime = eventTime;
}
private void updateTimeDebouncing(long eventTime) {
mCurrentKeyTime += eventTime - mLastMoveTime;
mLastMoveTime = eventTime;
}
private void resetTimeDebouncing(long eventTime, int currentKey) {
mLastKey = currentKey;
mLastKeyTime = mCurrentKeyTime + eventTime - mLastMoveTime;
mCurrentKeyTime = 0;
mLastMoveTime = eventTime;
}
private boolean isMinorTimeBounce() {
return mCurrentKeyTime < mLastKeyTime && mCurrentKeyTime < KEY_DEBOUNCE_TIME
&& mLastKey != NOT_A_KEY;
}
private void showKeyPreviewAndUpdateKey(int keyIndex) { private void showKeyPreviewAndUpdateKey(int keyIndex) {
updateKey(keyIndex); updateKey(keyIndex);
// The modifier key, such as shift key, should not be shown as preview when multi-touch is // The modifier key, such as shift key, should not be shown as preview when multi-touch is
@ -497,7 +506,8 @@ public class PointerTracker {
} }
private void debugLog(String title, int x, int y) { private void debugLog(String title, int x, int y) {
Key key = getKey(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null)); int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
Key key = getKey(keyIndex);
final String code; final String code;
if (key == null) { if (key == null) {
code = "----"; code = "----";
@ -505,7 +515,7 @@ public class PointerTracker {
int primaryCode = key.codes[0]; int primaryCode = key.codes[0];
code = String.format((primaryCode < 0) ? "%4d" : "0x%02x", primaryCode); code = String.format((primaryCode < 0) ? "%4d" : "0x%02x", primaryCode);
} }
Log.d(TAG, String.format("%s [%d] %3d,%3d %s %s", title, mPointerId, x, y, code, Log.d(TAG, String.format("%s [%d] %3d,%3d %3d(%s) %s", title, mPointerId, x, y, keyIndex,
isModifier() ? "modifier" : "")); code, isModifier() ? "modifier" : ""));
} }
} }