am 630d9c95: Introduce typing aware gesture detection
* commit '630d9c95e596c902b80dcb57eb0386e94290406d': Introduce typing aware gesture detectionmain
commit
022c8ffc05
|
@ -318,12 +318,13 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
|
||||||
return hasMessages(MSG_TYPING_STATE_EXPIRED);
|
return hasMessages(MSG_TYPING_STATE_EXPIRED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove "gesture off while fast typing" related dead code.
|
||||||
@Override
|
@Override
|
||||||
public void startGestureOffWhileFastTypingTimer() {
|
public void startGestureOffWhileFastTypingTimer() {
|
||||||
removeMessages(MSG_DISABLE_GESTURE_EXPIRED);
|
// removeMessages(MSG_DISABLE_GESTURE_EXPIRED);
|
||||||
PointerTracker.setGestureOffWhileFastTyping();
|
// PointerTracker.setGestureOffWhileFastTyping();
|
||||||
sendMessageDelayed(obtainMessage(MSG_DISABLE_GESTURE_EXPIRED),
|
// sendMessageDelayed(obtainMessage(MSG_DISABLE_GESTURE_EXPIRED),
|
||||||
mDisableGestureWhileFastTypingTimeout);
|
// mDisableGestureWhileFastTypingTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -169,6 +169,7 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
private boolean mIsDetectingGesture = false; // per PointerTracker.
|
private boolean mIsDetectingGesture = false; // per PointerTracker.
|
||||||
private static boolean sInGesture = false;
|
private static boolean sInGesture = false;
|
||||||
private static long sGestureFirstDownTime;
|
private static long sGestureFirstDownTime;
|
||||||
|
private static long sLastLetterTypingUpTime;
|
||||||
private static final InputPointers sAggregratedPointers = new InputPointers(
|
private static final InputPointers sAggregratedPointers = new InputPointers(
|
||||||
GestureStroke.DEFAULT_CAPACITY);
|
GestureStroke.DEFAULT_CAPACITY);
|
||||||
private static int sLastRecognitionPointSize = 0;
|
private static int sLastRecognitionPointSize = 0;
|
||||||
|
@ -698,6 +699,7 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
|
|
||||||
private void onGestureDownEvent(final int x, final int y, final long eventTime) {
|
private void onGestureDownEvent(final int x, final int y, final long eventTime) {
|
||||||
mIsDetectingGesture = true;
|
mIsDetectingGesture = true;
|
||||||
|
mGestureStrokeWithPreviewPoints.setLastLetterTypingTime(eventTime, sLastLetterTypingUpTime);
|
||||||
final int elapsedTimeFromFirstDown = (int)(eventTime - sGestureFirstDownTime);
|
final int elapsedTimeFromFirstDown = (int)(eventTime - sGestureFirstDownTime);
|
||||||
mGestureStrokeWithPreviewPoints.addPoint(x, y, elapsedTimeFromFirstDown,
|
mGestureStrokeWithPreviewPoints.addPoint(x, y, elapsedTimeFromFirstDown,
|
||||||
true /* isMajorEvent */);
|
true /* isMajorEvent */);
|
||||||
|
@ -842,7 +844,7 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
if (ProductionFlag.IS_EXPERIMENTAL) {
|
if (ProductionFlag.IS_EXPERIMENTAL) {
|
||||||
ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
|
ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
|
||||||
}
|
}
|
||||||
onUpEventInternal();
|
onUpEventInternal(eventTime);
|
||||||
onDownEventInternal(x, y, eventTime);
|
onDownEventInternal(x, y, eventTime);
|
||||||
} else {
|
} else {
|
||||||
// HACK: If there are currently multiple touches, register the key even if
|
// HACK: If there are currently multiple touches, register the key even if
|
||||||
|
@ -852,7 +854,7 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
// this hack.
|
// this hack.
|
||||||
if (getActivePointerTrackerCount() > 1 && sPointerTrackerQueue != null
|
if (getActivePointerTrackerCount() > 1 && sPointerTrackerQueue != null
|
||||||
&& !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
|
&& !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
|
||||||
onUpEventInternal();
|
onUpEventInternal(eventTime);
|
||||||
}
|
}
|
||||||
if (!mIsDetectingGesture) {
|
if (!mIsDetectingGesture) {
|
||||||
mKeyAlreadyProcessed = true;
|
mKeyAlreadyProcessed = true;
|
||||||
|
@ -897,7 +899,7 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onUpEventInternal();
|
onUpEventInternal(eventTime);
|
||||||
if (queue != null) {
|
if (queue != null) {
|
||||||
queue.remove(this);
|
queue.remove(this);
|
||||||
}
|
}
|
||||||
|
@ -911,11 +913,11 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
if (DEBUG_EVENT) {
|
if (DEBUG_EVENT) {
|
||||||
printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime);
|
printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime);
|
||||||
}
|
}
|
||||||
onUpEventInternal();
|
onUpEventInternal(eventTime);
|
||||||
mKeyAlreadyProcessed = true;
|
mKeyAlreadyProcessed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onUpEventInternal() {
|
private void onUpEventInternal(final long eventTime) {
|
||||||
mTimerProxy.cancelKeyTimers();
|
mTimerProxy.cancelKeyTimers();
|
||||||
mIsInSlidingKeyInput = false;
|
mIsInSlidingKeyInput = false;
|
||||||
mIsDetectingGesture = false;
|
mIsDetectingGesture = false;
|
||||||
|
@ -943,6 +945,10 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
}
|
}
|
||||||
if (currentKey != null && !currentKey.isRepeatable()) {
|
if (currentKey != null && !currentKey.isRepeatable()) {
|
||||||
detectAndSendKey(currentKey, mKeyX, mKeyY);
|
detectAndSendKey(currentKey, mKeyX, mKeyY);
|
||||||
|
final int code = currentKey.mCode;
|
||||||
|
if (Keyboard.isLetterCode(code) && code != Keyboard.CODE_SPACE) {
|
||||||
|
sLastLetterTypingUpTime = eventTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,10 @@ public class GestureStroke {
|
||||||
private long mLastMajorEventTime;
|
private long mLastMajorEventTime;
|
||||||
private int mLastMajorEventX;
|
private int mLastMajorEventX;
|
||||||
private int mLastMajorEventY;
|
private int mLastMajorEventY;
|
||||||
|
private boolean mAfterFastTyping;
|
||||||
|
|
||||||
private int mKeyWidth;
|
private int mKeyWidth;
|
||||||
|
private int mStartGestureLengthThresholdAfterFastTyping; // pixel
|
||||||
private int mStartGestureLengthThreshold; // pixel
|
private int mStartGestureLengthThreshold; // pixel
|
||||||
private int mMinGestureSamplingLength; // pixel
|
private int mMinGestureSamplingLength; // pixel
|
||||||
private int mGestureRecognitionSpeedThreshold; // pixel / sec
|
private int mGestureRecognitionSpeedThreshold; // pixel / sec
|
||||||
|
@ -45,7 +47,11 @@ public class GestureStroke {
|
||||||
private int mDetectFastMoveY;
|
private int mDetectFastMoveY;
|
||||||
|
|
||||||
// TODO: Move some of these to resource.
|
// TODO: Move some of these to resource.
|
||||||
private static final float START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH = 0.60f;
|
private static final int GESTURE_AFTER_FAST_TYPING_DURATION_THRESHOLD = 350; // msec
|
||||||
|
private static final float START_GESTURE_LENGTH_THRESHOLD_AFTER_FAST_TYPING_RATIO_TO_KEY_WIDTH =
|
||||||
|
8.0f;
|
||||||
|
private static final int START_GESTURE_LENGTH_THRESHOLD_DECAY_DURATION = 400; // msec
|
||||||
|
private static final float START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH = 0.6f;
|
||||||
private static final int START_GESTURE_DURATION_THRESHOLD = 70; // msec
|
private static final int START_GESTURE_DURATION_THRESHOLD = 70; // msec
|
||||||
private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec
|
private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec
|
||||||
private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH = 1.0f / 6.0f;
|
private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH = 1.0f / 6.0f;
|
||||||
|
@ -67,6 +73,8 @@ public class GestureStroke {
|
||||||
public void setKeyboardGeometry(final int keyWidth) {
|
public void setKeyboardGeometry(final int keyWidth) {
|
||||||
mKeyWidth = keyWidth;
|
mKeyWidth = keyWidth;
|
||||||
// TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
|
// TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
|
||||||
|
mStartGestureLengthThresholdAfterFastTyping = (int)(keyWidth
|
||||||
|
* START_GESTURE_LENGTH_THRESHOLD_AFTER_FAST_TYPING_RATIO_TO_KEY_WIDTH);
|
||||||
mStartGestureLengthThreshold =
|
mStartGestureLengthThreshold =
|
||||||
(int)(keyWidth * START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH);
|
(int)(keyWidth * START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH);
|
||||||
mMinGestureSamplingLength = (int)(keyWidth * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH);
|
mMinGestureSamplingLength = (int)(keyWidth * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH);
|
||||||
|
@ -75,10 +83,33 @@ public class GestureStroke {
|
||||||
mDetectFastMoveSpeedThreshold =
|
mDetectFastMoveSpeedThreshold =
|
||||||
(int)(keyWidth * DETECT_FAST_MOVE_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH);
|
(int)(keyWidth * DETECT_FAST_MOVE_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "setKeyboardGeometry: keyWidth=" + keyWidth);
|
Log.d(TAG, "[" + mPointerId + "] setKeyboardGeometry: keyWidth=" + keyWidth
|
||||||
|
+ " tL0=" + mStartGestureLengthThresholdAfterFastTyping
|
||||||
|
+ " tL=" + mStartGestureLengthThreshold);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLastLetterTypingTime(final long downTime, final long lastTypingTime) {
|
||||||
|
final long elpasedTimeAfterTyping = downTime - lastTypingTime;
|
||||||
|
if (elpasedTimeAfterTyping < GESTURE_AFTER_FAST_TYPING_DURATION_THRESHOLD) {
|
||||||
|
mAfterFastTyping = true;
|
||||||
|
}
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "[" + mPointerId + "] setLastTypingTime: dT=" + elpasedTimeAfterTyping
|
||||||
|
+ " afterFastTyping=" + mAfterFastTyping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getStartGestureLengthThreshold(final int deltaTime) {
|
||||||
|
if (!mAfterFastTyping || deltaTime >= START_GESTURE_LENGTH_THRESHOLD_DECAY_DURATION) {
|
||||||
|
return mStartGestureLengthThreshold;
|
||||||
|
}
|
||||||
|
final int decayedThreshold =
|
||||||
|
(mStartGestureLengthThresholdAfterFastTyping - mStartGestureLengthThreshold)
|
||||||
|
* deltaTime / START_GESTURE_LENGTH_THRESHOLD_DECAY_DURATION;
|
||||||
|
return mStartGestureLengthThresholdAfterFastTyping - decayedThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isStartOfAGesture() {
|
public boolean isStartOfAGesture() {
|
||||||
if (mDetectFastMoveTime == 0) {
|
if (mDetectFastMoveTime == 0) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -92,10 +123,12 @@ public class GestureStroke {
|
||||||
final int deltaLength = getDistance(
|
final int deltaLength = getDistance(
|
||||||
mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex),
|
mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex),
|
||||||
mDetectFastMoveX, mDetectFastMoveY);
|
mDetectFastMoveX, mDetectFastMoveY);
|
||||||
|
final int startGestureLengthThreshold = getStartGestureLengthThreshold(deltaTime);
|
||||||
final boolean isStartOfAGesture = deltaTime > START_GESTURE_DURATION_THRESHOLD
|
final boolean isStartOfAGesture = deltaTime > START_GESTURE_DURATION_THRESHOLD
|
||||||
&& deltaLength > mStartGestureLengthThreshold;
|
&& deltaLength > startGestureLengthThreshold;
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "isStartOfAGesture: dT=" + deltaTime + " dL=" + deltaLength
|
Log.d(TAG, "[" + mPointerId + "] isStartOfAGesture: dT=" + deltaTime
|
||||||
|
+ " dL=" + deltaLength + " tL=" + startGestureLengthThreshold
|
||||||
+ " points=" + size + (isStartOfAGesture ? " Detect start of a gesture" : ""));
|
+ " points=" + size + (isStartOfAGesture ? " Detect start of a gesture" : ""));
|
||||||
}
|
}
|
||||||
return isStartOfAGesture;
|
return isStartOfAGesture;
|
||||||
|
@ -109,6 +142,7 @@ public class GestureStroke {
|
||||||
mYCoordinates.setLength(0);
|
mYCoordinates.setLength(0);
|
||||||
mLastMajorEventTime = 0;
|
mLastMajorEventTime = 0;
|
||||||
mDetectFastMoveTime = 0;
|
mDetectFastMoveTime = 0;
|
||||||
|
mAfterFastTyping = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendPoint(final int x, final int y, final int time) {
|
private void appendPoint(final int x, final int y, final int time) {
|
||||||
|
@ -135,12 +169,13 @@ public class GestureStroke {
|
||||||
final int pixelsPerSec = pixels * MSEC_PER_SEC;
|
final int pixelsPerSec = pixels * MSEC_PER_SEC;
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
|
final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
|
||||||
Log.d(TAG, String.format("Speed=%.3f keyWidth/sec", speed));
|
Log.d(TAG, String.format("[" + mPointerId + "] speed=%.3f", speed));
|
||||||
}
|
}
|
||||||
// Equivalent to (pixels / msecs < mStartSpeedThreshold / MSEC_PER_SEC)
|
// Equivalent to (pixels / msecs < mStartSpeedThreshold / MSEC_PER_SEC)
|
||||||
if (mDetectFastMoveTime == 0 && pixelsPerSec > mDetectFastMoveSpeedThreshold * msecs) {
|
if (mDetectFastMoveTime == 0 && pixelsPerSec > mDetectFastMoveSpeedThreshold * msecs) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Detect fast move: T=" + time + " points = " + size);
|
Log.d(TAG, "[" + mPointerId + "] detect fast move: T="
|
||||||
|
+ time + " points = " + size);
|
||||||
}
|
}
|
||||||
mDetectFastMoveTime = time;
|
mDetectFastMoveTime = time;
|
||||||
mDetectFastMoveX = x;
|
mDetectFastMoveX = x;
|
||||||
|
|
Loading…
Reference in New Issue