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
private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
private static final int MULTITAP_INTERVAL = 800; // milliseconds
private static final int KEY_DEBOUNCE_TIME = 70;
// Miscellaneous constants
private static final int NOT_A_KEY = LatinKeyboardBaseView.NOT_A_KEY;
@ -57,10 +56,7 @@ public class PointerTracker {
private Key[] mKeys;
private int mKeyHysteresisDistanceSquared = -1;
private int mCurrentKey = NOT_A_KEY;
private int mStartX;
private int mStartY;
private long mDownTime;
private final KeyState mKeyState;
// true if event is already translated to a key action (long press or mini-keyboard)
private boolean mKeyAlreadyProcessed;
@ -68,18 +64,6 @@ public class PointerTracker {
// true if this pointer is repeatable key
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
private int mLastSentIndex;
private int mTapCount;
@ -90,6 +74,95 @@ public class PointerTracker {
// pressed 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,
boolean hasDistinctMultitouch) {
if (proxy == null || handler == null || keyDetector == null)
@ -98,6 +171,7 @@ public class PointerTracker {
mProxy = proxy;
mHandler = handler;
mKeyDetector = keyDetector;
mKeyState = new KeyState(keyDetector);
mHasDistinctMultitouch = hasDistinctMultitouch;
resetMultiTap();
}
@ -112,7 +186,7 @@ public class PointerTracker {
mKeys = keys;
mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance);
// Update current key index because keyboard layout has been changed.
mCurrentKey = mKeyDetector.getKeyIndexAndNearbyCodes(mStartX, mStartY, null);
mKeyState.onSetKeyboard();
}
private boolean isValidKeyIndex(int keyIndex) {
@ -133,7 +207,7 @@ public class PointerTracker {
}
public boolean isModifier() {
return isModifierInternal(mCurrentKey);
return isModifierInternal(mKeyState.getKeyIndex());
}
public boolean isOnModifierKey(int x, int y) {
@ -190,21 +264,16 @@ public class PointerTracker {
public void onDownEvent(int x, int y, long eventTime) {
if (DEBUG)
debugLog("onDownEvent:", x, y);
int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
mCurrentKey = keyIndex;
mStartX = x;
mStartY = y;
mDownTime = eventTime;
int keyIndex = mKeyState.onDownKey(x, y, eventTime);
mKeyAlreadyProcessed = false;
mIsRepeatableKey = false;
startMoveDebouncing(x, y);
startTimeDebouncing(eventTime);
checkMultiTap(eventTime, keyIndex);
if (mListener != null) {
int primaryCode = isValidKeyIndex(keyIndex) ? mKeys[keyIndex].codes[0] : 0;
mListener.onPress(primaryCode);
// This onPress call may have changed keyboard layout and have updated mCurrentKey
keyIndex = mCurrentKey;
// This onPress call may have changed keyboard layout and have updated mKeyIndex.
// If that's the case, mKeyIndex has been updated in setKeyboard().
keyIndex = mKeyState.getKeyIndex();
}
if (isValidKeyIndex(keyIndex)) {
if (mKeys[keyIndex].repeatable) {
@ -215,7 +284,6 @@ public class PointerTracker {
mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this);
}
showKeyPreviewAndUpdateKey(keyIndex);
updateMoveDebouncing(x, y);
}
public void onMoveEvent(int x, int y, long eventTime) {
@ -223,44 +291,28 @@ public class PointerTracker {
debugLog("onMoveEvent:", x, y);
if (mKeyAlreadyProcessed)
return;
int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
KeyState keyState = mKeyState;
int keyIndex = keyState.onMoveKey(x, y);
if (isValidKeyIndex(keyIndex)) {
if (mCurrentKey == NOT_A_KEY) {
updateTimeDebouncing(eventTime);
mCurrentKey = keyIndex;
if (keyState.getKeyIndex() == NOT_A_KEY) {
keyState.onMoveToNewKey(keyIndex, x, y);
mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this);
} else if (isMinorMoveBounce(x, y, keyIndex, mCurrentKey)) {
updateTimeDebouncing(eventTime);
} else {
} else if (!isMinorMoveBounce(x, y, keyIndex)) {
resetMultiTap();
resetTimeDebouncing(eventTime, mCurrentKey);
resetMoveDebouncing();
mCurrentKey = keyIndex;
keyState.onMoveToNewKey(keyIndex, x, y);
mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this);
}
} else {
if (mCurrentKey != NOT_A_KEY) {
updateTimeDebouncing(eventTime);
mCurrentKey = keyIndex;
if (keyState.getKeyIndex() != NOT_A_KEY) {
keyState.onMoveToNewKey(keyIndex, x ,y);
mHandler.cancelLongPressTimer();
} else if (isMinorMoveBounce(x, y, keyIndex, mCurrentKey)) {
updateTimeDebouncing(eventTime);
} else {
} else if (!isMinorMoveBounce(x, y, keyIndex)) {
resetMultiTap();
resetTimeDebouncing(eventTime, mCurrentKey);
resetMoveDebouncing();
mCurrentKey = keyIndex;
keyState.onMoveToNewKey(keyIndex, x ,y);
mHandler.cancelLongPressTimer();
}
}
/*
* 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);
showKeyPreviewAndUpdateKey(mKeyState.getKeyIndex());
}
public void onUpEvent(int x, int y, long eventTime) {
@ -270,23 +322,18 @@ public class PointerTracker {
return;
mHandler.cancelKeyTimers();
mHandler.cancelPopupPreview();
int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
if (isMinorMoveBounce(x, y, keyIndex, mCurrentKey)) {
updateTimeDebouncing(eventTime);
} else {
resetMultiTap();
resetTimeDebouncing(eventTime, mCurrentKey);
mCurrentKey = keyIndex;
}
if (isMinorTimeBounce()) {
mCurrentKey = mLastKey;
x = mLastCodeX;
y = mLastCodeY;
int keyIndex = mKeyState.onUpKey(x, y);
if (isMinorMoveBounce(x, y, keyIndex)) {
// Use previous fixed key index and coordinates.
keyIndex = mKeyState.getKeyIndex();
x = mKeyState.getKeyX();
y = mKeyState.getKeyY();
}
showKeyPreviewAndUpdateKey(NOT_A_KEY);
if (!mIsRepeatableKey) {
detectAndSendKey(mCurrentKey, x, y, eventTime);
detectAndSendKey(keyIndex, x, y, eventTime);
}
if (isValidKeyIndex(keyIndex))
mProxy.invalidateKey(mKeys[keyIndex]);
}
@ -297,7 +344,7 @@ public class PointerTracker {
mHandler.cancelKeyTimers();
mHandler.cancelPopupPreview();
showKeyPreviewAndUpdateKey(NOT_A_KEY);
int keyIndex = mCurrentKey;
int keyIndex = mKeyState.getKeyIndex();
if (isValidKeyIndex(keyIndex))
mProxy.invalidateKey(mKeys[keyIndex]);
}
@ -312,44 +359,30 @@ public class PointerTracker {
}
public int getLastX() {
return mLastX;
return mKeyState.getLastX();
}
public int getLastY() {
return mLastY;
return mKeyState.getLastY();
}
public long getDownTime() {
return mDownTime;
return mKeyState.getDownTime();
}
// These package scope methods are only for debugging purpose.
/* package */ int getStartX() {
return mStartX;
return mKeyState.getStartX();
}
/* package */ int getStartY() {
return mStartY;
return mKeyState.getStartY();
}
private void startMoveDebouncing(int x, int y) {
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) {
private boolean isMinorMoveBounce(int x, int y, int newKey) {
if (mKeys == null || mKeyHysteresisDistanceSquared < 0)
throw new IllegalStateException("keyboard and/or hysteresis not set");
int curKey = mKeyState.getKeyIndex();
if (newKey == curKey) {
return true;
} else if (isValidKeyIndex(curKey)) {
@ -371,30 +404,6 @@ public class PointerTracker {
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) {
updateKey(keyIndex);
// 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) {
Key key = getKey(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null));
int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
Key key = getKey(keyIndex);
final String code;
if (key == null) {
code = "----";
@ -505,7 +515,7 @@ public class PointerTracker {
int primaryCode = key.codes[0];
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,
isModifier() ? "modifier" : ""));
Log.d(TAG, String.format("%s [%d] %3d,%3d %3d(%s) %s", title, mPointerId, x, y, keyIndex,
code, isModifier() ? "modifier" : ""));
}
}
}