Merge "Fix gesture detection algorithm" into jb-mr1-dev
commit
6052a81e08
|
@ -172,8 +172,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
|||
private static long sLastLetterTypingUpTime;
|
||||
private static final InputPointers sAggregratedPointers = new InputPointers(
|
||||
GestureStroke.DEFAULT_CAPACITY);
|
||||
private static int sLastRecognitionPointSize = 0;
|
||||
private static long sLastRecognitionTime = 0;
|
||||
private static int sLastRecognitionPointSize = 0; // synchronized using sAggregratedPointers
|
||||
private static long sLastRecognitionTime = 0; // synchronized using sAggregratedPointers
|
||||
|
||||
// The position and time at which first down event occurred.
|
||||
private long mDownTime;
|
||||
|
@ -310,9 +310,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
|||
}
|
||||
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
|
||||
if (DEBUG_LISTENER) {
|
||||
Log.d(TAG, "onPress : " + KeyDetector.printableCode(key)
|
||||
+ " ignoreModifier=" + ignoreModifierKey
|
||||
+ " enabled=" + key.isEnabled());
|
||||
Log.d(TAG, String.format("[%d] onPress : %s%s%s", mPointerId,
|
||||
KeyDetector.printableCode(key),
|
||||
ignoreModifierKey ? " ignoreModifier" : "",
|
||||
key.isEnabled() ? "" : " disabled"));
|
||||
}
|
||||
if (ignoreModifierKey) {
|
||||
return false;
|
||||
|
@ -335,10 +336,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
|||
final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
|
||||
final int code = altersCode ? key.getAltCode() : primaryCode;
|
||||
if (DEBUG_LISTENER) {
|
||||
Log.d(TAG, "onCodeInput: " + Keyboard.printableCode(code)
|
||||
+ " text=" + key.getOutputText() + " x=" + x + " y=" + y
|
||||
+ " ignoreModifier=" + ignoreModifierKey + " altersCode=" + altersCode
|
||||
+ " enabled=" + key.isEnabled());
|
||||
final String output = code == Keyboard.CODE_OUTPUT_TEXT
|
||||
? key.getOutputText() : Keyboard.printableCode(code);
|
||||
Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s", mPointerId, x, y,
|
||||
output, ignoreModifierKey ? " ignoreModifier" : "",
|
||||
altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled"));
|
||||
}
|
||||
if (ProductionFlag.IS_EXPERIMENTAL) {
|
||||
ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey,
|
||||
|
@ -367,9 +369,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
|||
}
|
||||
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
|
||||
if (DEBUG_LISTENER) {
|
||||
Log.d(TAG, "onRelease : " + Keyboard.printableCode(primaryCode)
|
||||
+ " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey
|
||||
+ " enabled="+ key.isEnabled());
|
||||
Log.d(TAG, String.format("[%d] onRelease : %s%s%s%s", mPointerId,
|
||||
Keyboard.printableCode(primaryCode),
|
||||
withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "",
|
||||
key.isEnabled() ? "": " disabled"));
|
||||
}
|
||||
if (ProductionFlag.IS_EXPERIMENTAL) {
|
||||
ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding,
|
||||
|
@ -385,7 +388,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
|||
|
||||
private void callListenerOnCancelInput() {
|
||||
if (DEBUG_LISTENER) {
|
||||
Log.d(TAG, "onCancelInput");
|
||||
Log.d(TAG, String.format("[%d] onCancelInput", mPointerId));
|
||||
}
|
||||
if (ProductionFlag.IS_EXPERIMENTAL) {
|
||||
ResearchLogger.pointerTracker_callListenerOnCancelInput();
|
||||
|
@ -394,6 +397,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
|||
}
|
||||
|
||||
private void setKeyDetectorInner(final KeyDetector keyDetector) {
|
||||
final Keyboard keyboard = keyDetector.getKeyboard();
|
||||
if (keyDetector == mKeyDetector && keyboard == mKeyboard) {
|
||||
return;
|
||||
}
|
||||
mKeyDetector = keyDetector;
|
||||
mKeyboard = keyDetector.getKeyboard();
|
||||
mGestureStrokeWithPreviewPoints.setKeyboardGeometry(mKeyboard.mMostCommonKeyWidth);
|
||||
|
@ -564,10 +571,15 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
|||
return;
|
||||
}
|
||||
if (DEBUG_LISTENER) {
|
||||
Log.d(TAG, "onStartBatchInput");
|
||||
Log.d(TAG, String.format("[%d] onStartBatchInput", mPointerId));
|
||||
}
|
||||
sInGesture = true;
|
||||
synchronized (sAggregratedPointers) {
|
||||
sAggregratedPointers.reset();
|
||||
sLastRecognitionPointSize = 0;
|
||||
sLastRecognitionTime = 0;
|
||||
mListener.onStartBatchInput();
|
||||
}
|
||||
final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
|
||||
mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
|
||||
}
|
||||
|
@ -582,7 +594,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
|||
sLastRecognitionPointSize = size;
|
||||
sLastRecognitionTime = eventTime;
|
||||
if (DEBUG_LISTENER) {
|
||||
Log.d(TAG, "onUpdateBatchInput: batchPoints=" + size);
|
||||
Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d",
|
||||
mPointerId, size));
|
||||
}
|
||||
mListener.onUpdateBatchInput(sAggregratedPointers);
|
||||
}
|
||||
|
@ -595,37 +608,20 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
|||
private void mayEndBatchInput(final long eventTime) {
|
||||
synchronized (sAggregratedPointers) {
|
||||
mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
|
||||
mGestureStrokeWithPreviewPoints.reset();
|
||||
if (getActivePointerTrackerCount() == 1) {
|
||||
if (DEBUG_LISTENER) {
|
||||
Log.d(TAG, "onEndBatchInput: batchPoints="
|
||||
+ sAggregratedPointers.getPointerSize());
|
||||
Log.d(TAG, String.format("[%d] onEndBatchInput : batchPoints=%d",
|
||||
mPointerId, sAggregratedPointers.getPointerSize()));
|
||||
}
|
||||
sInGesture = false;
|
||||
sLastBatchInputTime = eventTime;
|
||||
mListener.onEndBatchInput(sAggregratedPointers);
|
||||
clearBatchInputPointsOfAllPointerTrackers();
|
||||
}
|
||||
}
|
||||
final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
|
||||
mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
|
||||
}
|
||||
|
||||
private static void abortBatchInput() {
|
||||
clearBatchInputPointsOfAllPointerTrackers();
|
||||
}
|
||||
|
||||
private static void clearBatchInputPointsOfAllPointerTrackers() {
|
||||
final int trackersSize = sTrackers.size();
|
||||
for (int i = 0; i < trackersSize; ++i) {
|
||||
final PointerTracker tracker = sTrackers.get(i);
|
||||
tracker.mGestureStrokeWithPreviewPoints.reset();
|
||||
}
|
||||
sAggregratedPointers.reset();
|
||||
sLastRecognitionPointSize = 0;
|
||||
sLastRecognitionTime = 0;
|
||||
}
|
||||
|
||||
public void processMotionEvent(final int action, final int x, final int y, final long eventTime,
|
||||
final KeyEventHandler handler) {
|
||||
switch (action) {
|
||||
|
@ -695,18 +691,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
|||
if (getActivePointerTrackerCount() == 1) {
|
||||
sGestureFirstDownTime = eventTime;
|
||||
}
|
||||
onGestureDownEvent(x, y, eventTime);
|
||||
mGestureStrokeWithPreviewPoints.onDownEvent(x, y, eventTime, sGestureFirstDownTime,
|
||||
sLastLetterTypingUpTime);
|
||||
}
|
||||
}
|
||||
|
||||
private void onGestureDownEvent(final int x, final int y, final long eventTime) {
|
||||
mIsDetectingGesture = true;
|
||||
mGestureStrokeWithPreviewPoints.setLastLetterTypingTime(eventTime, sLastLetterTypingUpTime);
|
||||
final int elapsedTimeFromFirstDown = (int)(eventTime - sGestureFirstDownTime);
|
||||
mGestureStrokeWithPreviewPoints.addPoint(x, y, elapsedTimeFromFirstDown,
|
||||
true /* isMajorEvent */);
|
||||
}
|
||||
|
||||
private void onDownEventInternal(final int x, final int y, final long eventTime) {
|
||||
Key key = onDownKey(x, y, eventTime);
|
||||
// Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
|
||||
|
@ -939,9 +928,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
|||
mayEndBatchInput(eventTime);
|
||||
return;
|
||||
}
|
||||
// This event will be recognized as a regular code input. Clear unused possible batch points
|
||||
// so they are not mistakenly displayed as preview.
|
||||
clearBatchInputPointsOfAllPointerTrackers();
|
||||
|
||||
if (mKeyAlreadyProcessed) {
|
||||
return;
|
||||
}
|
||||
|
@ -955,7 +942,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
|||
}
|
||||
|
||||
public void onShowMoreKeysPanel(final int x, final int y, final KeyEventHandler handler) {
|
||||
abortBatchInput();
|
||||
onLongPressed();
|
||||
mIsShowingMoreKeysPanel = true;
|
||||
onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
|
||||
|
@ -1043,7 +1029,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
|||
final long eventTime) {
|
||||
final Key key = mKeyDetector.detectHitKey(x, y);
|
||||
final String code = KeyDetector.printableCode(key);
|
||||
Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %s", title,
|
||||
(mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, eventTime, code));
|
||||
Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
|
||||
(mKeyAlreadyProcessed ? "-" : " "), title, x, y, eventTime, code));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import com.android.inputmethod.latin.ResizableIntArray;
|
|||
public class GestureStroke {
|
||||
private static final String TAG = GestureStroke.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
private static final boolean DEBUG_SPEED = false;
|
||||
|
||||
public static final int DEFAULT_CAPACITY = 128;
|
||||
|
||||
|
@ -29,42 +30,52 @@ public class GestureStroke {
|
|||
private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY);
|
||||
private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
|
||||
private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
|
||||
private int mIncrementalRecognitionSize;
|
||||
private int mLastIncrementalBatchSize;
|
||||
private long mLastMajorEventTime;
|
||||
private int mLastMajorEventX;
|
||||
private int mLastMajorEventY;
|
||||
private boolean mAfterFastTyping;
|
||||
|
||||
private int mKeyWidth;
|
||||
private int mStartGestureLengthThresholdAfterFastTyping; // pixel
|
||||
private int mStartGestureLengthThreshold; // pixel
|
||||
private int mMinGestureSamplingLength; // pixel
|
||||
private int mGestureRecognitionSpeedThreshold; // pixel / sec
|
||||
private int mKeyWidth; // pixel
|
||||
// Static threshold for starting gesture detection
|
||||
private int mDetectFastMoveSpeedThreshold; // pixel /sec
|
||||
private int mDetectFastMoveTime;
|
||||
private int mDetectFastMoveX;
|
||||
private int mDetectFastMoveY;
|
||||
// Dynamic threshold for gesture after fast typing
|
||||
private boolean mAfterFastTyping;
|
||||
private int mGestureDynamicDistanceThresholdFrom; // pixel
|
||||
private int mGestureDynamicDistanceThresholdTo; // pixel
|
||||
// Variables for gesture sampling
|
||||
private int mGestureSamplingMinimumDistance; // pixel
|
||||
private long mLastMajorEventTime;
|
||||
private int mLastMajorEventX;
|
||||
private int mLastMajorEventY;
|
||||
// Variables for gesture recognition
|
||||
private int mGestureRecognitionSpeedThreshold; // pixel / sec
|
||||
private int mIncrementalRecognitionSize;
|
||||
private int mLastIncrementalBatchSize;
|
||||
|
||||
// TODO: Move some of these to resource.
|
||||
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 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 GESTURE_RECOGNITION_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH =
|
||||
5.5f; // keyWidth / sec
|
||||
private static final float DETECT_FAST_MOVE_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH =
|
||||
5.0f; // keyWidth / sec
|
||||
private static final int MSEC_PER_SEC = 1000;
|
||||
|
||||
public static final boolean hasRecognitionTimePast(
|
||||
final long currentTime, final long lastRecognitionTime) {
|
||||
return currentTime > lastRecognitionTime + MIN_GESTURE_RECOGNITION_TIME;
|
||||
}
|
||||
// Static threshold for gesture after fast typing
|
||||
public static final int GESTURE_STATIC_TIME_THRESHOLD_AFTER_FAST_TYPING = 350; // msec
|
||||
|
||||
// Static threshold for starting gesture detection
|
||||
private static final float DETECT_FAST_MOVE_SPEED_THRESHOLD = 1.5f; // keyWidth / sec
|
||||
|
||||
// Dynamic threshold for gesture after fast typing
|
||||
private static final int GESTURE_DYNAMIC_THRESHOLD_DECAY_DURATION = 450; // msec
|
||||
// Time based threshold values
|
||||
private static final int GESTURE_DYNAMIC_TIME_THRESHOLD_FROM = 300; // msec
|
||||
private static final int GESTURE_DYNAMIC_TIME_THRESHOLD_TO = 20; // msec
|
||||
// Distance based threshold values
|
||||
private static final float GESTURE_DYNAMIC_DISTANCE_THRESHOLD_FROM = 6.0f; // keyWidth
|
||||
private static final float GESTURE_DYNAMIC_DISTANCE_THRESHOLD_TO = 0.35f; // keyWidth
|
||||
|
||||
// Parameters for gesture sampling
|
||||
private static final float GESTURE_SAMPLING_MINIMUM_DISTANCE = 1.0f / 6.0f; // keyWidth
|
||||
|
||||
// Parameters for gesture recognition
|
||||
private static final int GESTURE_RECOGNITION_MINIMUM_TIME = 100; // msec
|
||||
private static final float GESTURE_RECOGNITION_SPEED_THRESHOLD = 5.5f; // keyWidth / sec
|
||||
|
||||
private static final int MSEC_PER_SEC = 1000;
|
||||
|
||||
public GestureStroke(final int pointerId) {
|
||||
mPointerId = pointerId;
|
||||
|
@ -73,41 +84,58 @@ public class GestureStroke {
|
|||
public void setKeyboardGeometry(final int keyWidth) {
|
||||
mKeyWidth = keyWidth;
|
||||
// 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 =
|
||||
(int)(keyWidth * START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH);
|
||||
mMinGestureSamplingLength = (int)(keyWidth * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH);
|
||||
mDetectFastMoveSpeedThreshold = (int)(keyWidth * DETECT_FAST_MOVE_SPEED_THRESHOLD);
|
||||
mGestureDynamicDistanceThresholdFrom =
|
||||
(int)(keyWidth * GESTURE_DYNAMIC_DISTANCE_THRESHOLD_FROM);
|
||||
mGestureDynamicDistanceThresholdTo =
|
||||
(int)(keyWidth * GESTURE_DYNAMIC_DISTANCE_THRESHOLD_TO);
|
||||
mGestureSamplingMinimumDistance = (int)(keyWidth * GESTURE_SAMPLING_MINIMUM_DISTANCE);
|
||||
mGestureRecognitionSpeedThreshold =
|
||||
(int)(keyWidth * GESTURE_RECOGNITION_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH);
|
||||
mDetectFastMoveSpeedThreshold =
|
||||
(int)(keyWidth * DETECT_FAST_MOVE_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH);
|
||||
(int)(keyWidth * GESTURE_RECOGNITION_SPEED_THRESHOLD);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "[" + mPointerId + "] setKeyboardGeometry: keyWidth=" + keyWidth
|
||||
+ " tL0=" + mStartGestureLengthThresholdAfterFastTyping
|
||||
+ " tL=" + mStartGestureLengthThreshold);
|
||||
Log.d(TAG, String.format(
|
||||
"[%d] setKeyboardGeometry: keyWidth=%3d tT=%3d >> %3d tD=%3d >> %3d",
|
||||
mPointerId, keyWidth,
|
||||
GESTURE_DYNAMIC_TIME_THRESHOLD_FROM,
|
||||
GESTURE_DYNAMIC_TIME_THRESHOLD_TO,
|
||||
mGestureDynamicDistanceThresholdFrom,
|
||||
mGestureDynamicDistanceThresholdTo));
|
||||
}
|
||||
}
|
||||
|
||||
public void setLastLetterTypingTime(final long downTime, final long lastTypingTime) {
|
||||
final long elpasedTimeAfterTyping = downTime - lastTypingTime;
|
||||
if (elpasedTimeAfterTyping < GESTURE_AFTER_FAST_TYPING_DURATION_THRESHOLD) {
|
||||
public void onDownEvent(final int x, final int y, final long downTime,
|
||||
final long gestureFirstDownTime, final long lastTypingTime) {
|
||||
reset();
|
||||
final long elapsedTimeAfterTyping = downTime - lastTypingTime;
|
||||
if (elapsedTimeAfterTyping < GESTURE_STATIC_TIME_THRESHOLD_AFTER_FAST_TYPING) {
|
||||
mAfterFastTyping = true;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "[" + mPointerId + "] setLastTypingTime: dT=" + elpasedTimeAfterTyping
|
||||
+ " afterFastTyping=" + mAfterFastTyping);
|
||||
Log.d(TAG, String.format("[%d] onDownEvent: dT=%3d%s", mPointerId,
|
||||
elapsedTimeAfterTyping, mAfterFastTyping ? " afterFastTyping" : ""));
|
||||
}
|
||||
final int elapsedTimeFromFirstDown = (int)(downTime - gestureFirstDownTime);
|
||||
addPoint(x, y, elapsedTimeFromFirstDown, true /* isMajorEvent */);
|
||||
}
|
||||
|
||||
private int getStartGestureLengthThreshold(final int deltaTime) {
|
||||
if (!mAfterFastTyping || deltaTime >= START_GESTURE_LENGTH_THRESHOLD_DECAY_DURATION) {
|
||||
return mStartGestureLengthThreshold;
|
||||
private int getGestureDynamicDistanceThreshold(final int deltaTime) {
|
||||
if (!mAfterFastTyping || deltaTime >= GESTURE_DYNAMIC_THRESHOLD_DECAY_DURATION) {
|
||||
return mGestureDynamicDistanceThresholdTo;
|
||||
}
|
||||
final int decayedThreshold =
|
||||
(mStartGestureLengthThresholdAfterFastTyping - mStartGestureLengthThreshold)
|
||||
* deltaTime / START_GESTURE_LENGTH_THRESHOLD_DECAY_DURATION;
|
||||
return mStartGestureLengthThresholdAfterFastTyping - decayedThreshold;
|
||||
(mGestureDynamicDistanceThresholdFrom - mGestureDynamicDistanceThresholdTo)
|
||||
* deltaTime / GESTURE_DYNAMIC_THRESHOLD_DECAY_DURATION;
|
||||
return mGestureDynamicDistanceThresholdFrom - decayedThreshold;
|
||||
}
|
||||
|
||||
private int getGestureDynamicTimeThreshold(final int deltaTime) {
|
||||
if (!mAfterFastTyping || deltaTime >= GESTURE_DYNAMIC_THRESHOLD_DECAY_DURATION) {
|
||||
return GESTURE_DYNAMIC_TIME_THRESHOLD_TO;
|
||||
}
|
||||
final int decayedThreshold =
|
||||
(GESTURE_DYNAMIC_TIME_THRESHOLD_FROM - GESTURE_DYNAMIC_TIME_THRESHOLD_TO)
|
||||
* deltaTime / GESTURE_DYNAMIC_THRESHOLD_DECAY_DURATION;
|
||||
return GESTURE_DYNAMIC_TIME_THRESHOLD_FROM - decayedThreshold;
|
||||
}
|
||||
|
||||
public boolean isStartOfAGesture() {
|
||||
|
@ -120,21 +148,24 @@ public class GestureStroke {
|
|||
}
|
||||
final int lastIndex = size - 1;
|
||||
final int deltaTime = mEventTimes.get(lastIndex) - mDetectFastMoveTime;
|
||||
final int deltaLength = getDistance(
|
||||
final int deltaDistance = getDistance(
|
||||
mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex),
|
||||
mDetectFastMoveX, mDetectFastMoveY);
|
||||
final int startGestureLengthThreshold = getStartGestureLengthThreshold(deltaTime);
|
||||
final boolean isStartOfAGesture = deltaTime > START_GESTURE_DURATION_THRESHOLD
|
||||
&& deltaLength > startGestureLengthThreshold;
|
||||
final int distanceThreshold = getGestureDynamicDistanceThreshold(deltaTime);
|
||||
final int timeThreshold = getGestureDynamicTimeThreshold(deltaTime);
|
||||
final boolean isStartOfAGesture = deltaTime >= timeThreshold
|
||||
&& deltaDistance >= distanceThreshold;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "[" + mPointerId + "] isStartOfAGesture: dT=" + deltaTime
|
||||
+ " dL=" + deltaLength + " tL=" + startGestureLengthThreshold
|
||||
+ " points=" + size + (isStartOfAGesture ? " Detect start of a gesture" : ""));
|
||||
Log.d(TAG, String.format("[%d] isStartOfAGesture: dT=%3d tT=%3d dD=%3d tD=%3d%s%s",
|
||||
mPointerId, deltaTime, timeThreshold,
|
||||
deltaDistance, distanceThreshold,
|
||||
mAfterFastTyping ? " afterFastTyping" : "",
|
||||
isStartOfAGesture ? " startOfAGesture" : ""));
|
||||
}
|
||||
return isStartOfAGesture;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
protected void reset() {
|
||||
mIncrementalRecognitionSize = 0;
|
||||
mLastIncrementalBatchSize = 0;
|
||||
mEventTimes.setLength(0);
|
||||
|
@ -167,15 +198,17 @@ public class GestureStroke {
|
|||
if (msecs > 0) {
|
||||
final int pixels = getDistance(lastX, lastY, x, y);
|
||||
final int pixelsPerSec = pixels * MSEC_PER_SEC;
|
||||
if (DEBUG) {
|
||||
if (DEBUG_SPEED) {
|
||||
final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
|
||||
Log.d(TAG, String.format("[" + mPointerId + "] speed=%.3f", speed));
|
||||
Log.d(TAG, String.format("[%d] detectFastMove: speed=%5.2f", mPointerId, speed));
|
||||
}
|
||||
// Equivalent to (pixels / msecs < mStartSpeedThreshold / MSEC_PER_SEC)
|
||||
if (mDetectFastMoveTime == 0 && pixelsPerSec > mDetectFastMoveSpeedThreshold * msecs) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "[" + mPointerId + "] detect fast move: T="
|
||||
+ time + " points = " + size);
|
||||
final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
|
||||
Log.d(TAG, String.format(
|
||||
"[%d] detectFastMove: speed=%5.2f T=%3d points=%3d fastMove",
|
||||
mPointerId, speed, time, size));
|
||||
}
|
||||
mDetectFastMoveTime = time;
|
||||
mDetectFastMoveX = x;
|
||||
|
@ -192,8 +225,8 @@ public class GestureStroke {
|
|||
appendPoint(x, y, time);
|
||||
updateMajorEvent(x, y, time);
|
||||
} else {
|
||||
final int dist = detectFastMove(x, y, time);
|
||||
if (dist > mMinGestureSamplingLength) {
|
||||
final int distance = detectFastMove(x, y, time);
|
||||
if (distance > mGestureSamplingMinimumDistance) {
|
||||
appendPoint(x, y, time);
|
||||
}
|
||||
}
|
||||
|
@ -216,6 +249,11 @@ public class GestureStroke {
|
|||
}
|
||||
}
|
||||
|
||||
public static final boolean hasRecognitionTimePast(
|
||||
final long currentTime, final long lastRecognitionTime) {
|
||||
return currentTime > lastRecognitionTime + GESTURE_RECOGNITION_MINIMUM_TIME;
|
||||
}
|
||||
|
||||
public void appendAllBatchPoints(final InputPointers out) {
|
||||
appendBatchPoints(out, mEventTimes.getLength());
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
protected void reset() {
|
||||
super.reset();
|
||||
mStrokeId++;
|
||||
mLastPreviewSize = 0;
|
||||
|
|
|
@ -1402,6 +1402,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
|
|||
|
||||
@Override
|
||||
public void onStartBatchInput() {
|
||||
BatchInputUpdater.getInstance().onStartBatchInput();
|
||||
mConnection.beginBatchEdit();
|
||||
if (mWordComposer.isComposingWord()) {
|
||||
if (ProductionFlag.IS_INTERNAL) {
|
||||
|
@ -1433,6 +1434,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
|
|||
private static final class BatchInputUpdater implements Handler.Callback {
|
||||
private final Handler mHandler;
|
||||
private LatinIME mLatinIme;
|
||||
private boolean mInBatchInput; // synchornized using "this".
|
||||
|
||||
private BatchInputUpdater() {
|
||||
final HandlerThread handlerThread = new HandlerThread(
|
||||
|
@ -1456,17 +1458,32 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
|
|||
public boolean handleMessage(final Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP:
|
||||
final SuggestedWords suggestedWords = getSuggestedWordsGesture(
|
||||
(InputPointers)msg.obj, mLatinIme);
|
||||
showGesturePreviewAndSuggestionStrip(
|
||||
suggestedWords, false /* dismissGestureFloatingPreviewText */, mLatinIme);
|
||||
updateBatchInput((InputPointers)msg.obj, mLatinIme);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void updateGesturePreviewAndSuggestionStrip(final InputPointers batchPointers,
|
||||
// Run in the UI thread.
|
||||
public synchronized void onStartBatchInput() {
|
||||
mInBatchInput = true;
|
||||
}
|
||||
|
||||
// Run in the Handler thread.
|
||||
private synchronized void updateBatchInput(final InputPointers batchPointers,
|
||||
final LatinIME latinIme) {
|
||||
if (!mInBatchInput) {
|
||||
// Batch input has ended while the message was being delivered.
|
||||
return;
|
||||
}
|
||||
final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(
|
||||
batchPointers, latinIme);
|
||||
latinIme.mHandler.showGesturePreviewAndSuggestionStrip(
|
||||
suggestedWords, false /* dismissGestureFloatingPreviewText */);
|
||||
}
|
||||
|
||||
// Run in the UI thread.
|
||||
public void onUpdateBatchInput(final InputPointers batchPointers, final LatinIME latinIme) {
|
||||
mLatinIme = latinIme;
|
||||
if (mHandler.hasMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP)) {
|
||||
return;
|
||||
|
@ -1476,15 +1493,20 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
|
|||
.sendToTarget();
|
||||
}
|
||||
|
||||
public void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
|
||||
final boolean dismissGestureFloatingPreviewText, final LatinIME latinIme) {
|
||||
// Run in the UI thread.
|
||||
public synchronized SuggestedWords onEndBatchInput(final InputPointers batchPointers,
|
||||
final LatinIME latinIme) {
|
||||
mInBatchInput = false;
|
||||
final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(
|
||||
batchPointers, latinIme);
|
||||
latinIme.mHandler.showGesturePreviewAndSuggestionStrip(
|
||||
suggestedWords, dismissGestureFloatingPreviewText);
|
||||
suggestedWords, true /* dismissGestureFloatingPreviewText */);
|
||||
return suggestedWords;
|
||||
}
|
||||
|
||||
// {@link LatinIME#getSuggestedWords(int)} method calls with same session id have to
|
||||
// be synchronized.
|
||||
public synchronized SuggestedWords getSuggestedWordsGesture(
|
||||
private static SuggestedWords getSuggestedWordsGestureLocked(
|
||||
final InputPointers batchPointers, final LatinIME latinIme) {
|
||||
latinIme.mWordComposer.setBatchInputPointers(batchPointers);
|
||||
return latinIme.getSuggestedWords(Suggest.SESSION_GESTURE);
|
||||
|
@ -1505,16 +1527,13 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
|
|||
|
||||
@Override
|
||||
public void onUpdateBatchInput(final InputPointers batchPointers) {
|
||||
BatchInputUpdater.getInstance().updateGesturePreviewAndSuggestionStrip(batchPointers, this);
|
||||
BatchInputUpdater.getInstance().onUpdateBatchInput(batchPointers, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEndBatchInput(final InputPointers batchPointers) {
|
||||
final BatchInputUpdater batchInputUpdater = BatchInputUpdater.getInstance();
|
||||
final SuggestedWords suggestedWords = batchInputUpdater.getSuggestedWordsGesture(
|
||||
final SuggestedWords suggestedWords = BatchInputUpdater.getInstance().onEndBatchInput(
|
||||
batchPointers, this);
|
||||
batchInputUpdater.showGesturePreviewAndSuggestionStrip(
|
||||
suggestedWords, true /* dismissGestureFloatingPreviewText */, this);
|
||||
final String batchInputText = (suggestedWords.size() > 0)
|
||||
? suggestedWords.getWord(0) : null;
|
||||
if (TextUtils.isEmpty(batchInputText)) {
|
||||
|
|
Loading…
Reference in New Issue