Fix detecting fast typing algorithm and parameter

Bug: 7032858
Change-Id: I5ed701e2394d16e43258a3c22d59991cb18adce8
main
Tadashi G. Takaoka 2012-10-05 14:37:22 +09:00
parent 4580b7e457
commit 3623b9767b
5 changed files with 101 additions and 22 deletions

View File

@ -144,6 +144,8 @@
<!-- Parameters for gesture recognition (msec) and (keyWidth%/sec) --> <!-- Parameters for gesture recognition (msec) and (keyWidth%/sec) -->
<attr name="gestureRecognitionMinimumTime" format="integer" /> <attr name="gestureRecognitionMinimumTime" format="integer" />
<attr name="gestureRecognitionSpeedThreshold" format="fraction" /> <attr name="gestureRecognitionSpeedThreshold" format="fraction" />
<!-- Suppress showing key preview duration after batch input in millisecond -->
<attr name="suppressKeyPreviewAfterBatchInputDuration" format="integer" />
</declare-styleable> </declare-styleable>
<declare-styleable name="SuggestionStripView"> <declare-styleable name="SuggestionStripView">

View File

@ -70,7 +70,7 @@
false --> false -->
<bool name="config_show_more_keys_keyboard_at_touched_point">false</bool> <bool name="config_show_more_keys_keyboard_at_touched_point">false</bool>
<!-- Static threshold for gesture after fast typing (msec) --> <!-- Static threshold for gesture after fast typing (msec) -->
<integer name="config_gesture_static_time_threshold_after_fast_typing">350</integer> <integer name="config_gesture_static_time_threshold_after_fast_typing">1000</integer>
<!-- Static threshold for starting gesture detection (keyWidth%/sec) --> <!-- Static threshold for starting gesture detection (keyWidth%/sec) -->
<fraction name="config_gesture_detect_fast_move_speed_threshold">150%</fraction> <fraction name="config_gesture_detect_fast_move_speed_threshold">150%</fraction>
<!-- Dynamic threshold for gesture after fast typing (msec) --> <!-- Dynamic threshold for gesture after fast typing (msec) -->
@ -86,6 +86,8 @@
<!-- Parameters for gesture recognition (msec) and (keyWidth%/sec) --> <!-- Parameters for gesture recognition (msec) and (keyWidth%/sec) -->
<integer name="config_gesture_recognition_minimum_time">100</integer> <integer name="config_gesture_recognition_minimum_time">100</integer>
<fraction name="config_gesture_recognition_speed_threshold">550%</fraction> <fraction name="config_gesture_recognition_speed_threshold">550%</fraction>
<!-- Suppress showing key preview duration after batch input in millisecond -->
<integer name="config_suppress_key_preview_after_batch_input_duration">1000</integer>
<!-- <!--
Configuration for auto correction Configuration for auto correction
--> -->

View File

@ -105,6 +105,7 @@
<item name="gestureSamplingMinimumDistance">@fraction/config_gesture_sampling_minimum_distance</item> <item name="gestureSamplingMinimumDistance">@fraction/config_gesture_sampling_minimum_distance</item>
<item name="gestureRecognitionMinimumTime">@integer/config_gesture_recognition_minimum_time</item> <item name="gestureRecognitionMinimumTime">@integer/config_gesture_recognition_minimum_time</item>
<item name="gestureRecognitionSpeedThreshold">@fraction/config_gesture_recognition_speed_threshold</item> <item name="gestureRecognitionSpeedThreshold">@fraction/config_gesture_recognition_speed_threshold</item>
<item name="suppressKeyPreviewAfterBatchInputDuration">@integer/config_suppress_key_preview_after_batch_input_duration</item>
</style> </style>
<style <style
name="MainKeyboardView" name="MainKeyboardView"

View File

@ -82,6 +82,17 @@ import java.util.WeakHashMap;
* @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout
* @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout
* @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint
* @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping
* @attr ref R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold
* @attr ref R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration
* @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom
* @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo
* @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom
* @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo
* @attr ref R.styleable#MainKeyboardView_gestureSamplingMinimumDistance
* @attr ref R.styleable#MainKeyboardView_gestureRecognitionMinimumTime
* @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold
* @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
*/ */
public final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler, public final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
SuddenJumpingTouchEventHandler.ProcessMotionEvent { SuddenJumpingTouchEventHandler.ProcessMotionEvent {

View File

@ -48,9 +48,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
private static boolean sGestureHandlingEnabledByInputField = false; private static boolean sGestureHandlingEnabledByInputField = false;
private static boolean sGestureHandlingEnabledByUser = false; private static boolean sGestureHandlingEnabledByUser = false;
// TODO: Move this to resource.
private static final int SUPPRESS_KEY_PREVIEW_AFTER_LAST_BATCH_INPUT_DURATION = 1000; // msec
public interface KeyEventHandler { public interface KeyEventHandler {
/** /**
* Get KeyDetector object that is used for this PointerTracker. * Get KeyDetector object that is used for this PointerTracker.
@ -126,6 +123,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
public final int mTouchNoiseThresholdTime; public final int mTouchNoiseThresholdTime;
public final float mTouchNoiseThresholdDistance; public final float mTouchNoiseThresholdDistance;
public final int mTouchNoiseThresholdDistanceSquared; public final int mTouchNoiseThresholdDistanceSquared;
public final int mSuppressKeyPreviewAfterBatchInputDuration;
public static final PointerTrackerParams DEFAULT = new PointerTrackerParams(); public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
@ -134,6 +132,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
mTouchNoiseThresholdTime = 0; mTouchNoiseThresholdTime = 0;
mTouchNoiseThresholdDistance = 0.0f; mTouchNoiseThresholdDistance = 0.0f;
mTouchNoiseThresholdDistanceSquared = 0; mTouchNoiseThresholdDistanceSquared = 0;
mSuppressKeyPreviewAfterBatchInputDuration = 0;
} }
public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) { public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) {
@ -146,6 +145,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
mTouchNoiseThresholdDistance = touchNouseThresholdDistance; mTouchNoiseThresholdDistance = touchNouseThresholdDistance;
mTouchNoiseThresholdDistanceSquared = mTouchNoiseThresholdDistanceSquared =
(int)(touchNouseThresholdDistance * touchNouseThresholdDistance); (int)(touchNouseThresholdDistance * touchNouseThresholdDistance);
mSuppressKeyPreviewAfterBatchInputDuration = mainKeyboardViewAttr.getInt(
R.styleable.MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration, 0);
} }
} }
@ -170,13 +171,80 @@ public final 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 sLastBatchInputTime; private static TimeRecorder sTimeRecorder;
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; // synchronized using sAggregratedPointers private static int sLastRecognitionPointSize = 0; // synchronized using sAggregratedPointers
private static long sLastRecognitionTime = 0; // synchronized using sAggregratedPointers private static long sLastRecognitionTime = 0; // synchronized using sAggregratedPointers
static final class TimeRecorder {
private final int mSuppressKeyPreviewAfterBatchInputDuration;
private final int mStaticTimeThresholdAfterFastTyping; // msec
private long mLastTypingTime;
private long mLastLetterTypingTime;
private long mLastBatchInputTime;
public TimeRecorder(final PointerTrackerParams pointerTrackerParams,
final GestureStrokeParams gestureStrokeParams) {
mSuppressKeyPreviewAfterBatchInputDuration =
pointerTrackerParams.mSuppressKeyPreviewAfterBatchInputDuration;
mStaticTimeThresholdAfterFastTyping =
gestureStrokeParams.mStaticTimeThresholdAfterFastTyping;
}
private void recordTyping(final long eventTime) {
mLastTypingTime = eventTime;
}
private void recordLetterTyping(final long eventTime) {
mLastLetterTypingTime = eventTime;
// Reset gesture typing time
mLastBatchInputTime = 0;
}
private void recordGestureTyping(final long eventTime) {
mLastBatchInputTime = eventTime;
// Reset typing time.
mLastTypingTime = 0;
}
private boolean isInTyping() {
return mLastTypingTime != 0;
}
private boolean isInBatchInput() {
return mLastBatchInputTime != 0;
}
public void onCodeInput(final int code, final long eventTime) {
if (Keyboard.isLetterCode(code) && code != Keyboard.CODE_SPACE) {
if (isInTyping()
&& eventTime - mLastTypingTime < mStaticTimeThresholdAfterFastTyping) {
recordLetterTyping(eventTime);
}
} else {
if (eventTime - mLastLetterTypingTime < mStaticTimeThresholdAfterFastTyping) {
// This non-letter typing should be treated as a part of fast typing.
recordLetterTyping(eventTime);
}
}
recordTyping(eventTime);
}
public void onEndBatchInput(final long eventTime) {
recordGestureTyping(eventTime);
}
public long getLastLetterTypingTime() {
return mLastLetterTypingTime;
}
public boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
return !isInTyping() && isInBatchInput()
&& eventTime - mLastBatchInputTime < mSuppressKeyPreviewAfterBatchInputDuration;
}
}
// The position and time at which first down event occurred. // The position and time at which first down event occurred.
private long mDownTime; private long mDownTime;
private long mUpTime; private long mUpTime;
@ -225,11 +293,13 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack; sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
sParams = PointerTrackerParams.DEFAULT; sParams = PointerTrackerParams.DEFAULT;
sGestureStrokeParams = GestureStrokeParams.DEFAULT; sGestureStrokeParams = GestureStrokeParams.DEFAULT;
sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
} }
public static void setParameters(final TypedArray mainKeyboardViewAttr) { public static void setParameters(final TypedArray mainKeyboardViewAttr) {
sParams = new PointerTrackerParams(mainKeyboardViewAttr); sParams = new PointerTrackerParams(mainKeyboardViewAttr);
sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr); sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
} }
private static void updateGestureHandlingMode() { private static void updateGestureHandlingMode() {
@ -336,7 +406,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
// Note that we need primaryCode argument because the keyboard may in shifted state and the // Note that we need primaryCode argument because the keyboard may in shifted state and the
// primaryCode is different from {@link Key#mCode}. // primaryCode is different from {@link Key#mCode}.
private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x, private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
final int y) { final int y, final long eventTime) {
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier(); final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState(); final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
final int code = altersCode ? key.getAltCode() : primaryCode; final int code = altersCode ? key.getAltCode() : primaryCode;
@ -356,7 +426,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
} }
// Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state. // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
if (key.isEnabled() || altersCode) { if (key.isEnabled() || altersCode) {
sLastBatchInputTime = 0; // reset time sTimeRecorder.onCodeInput(code, eventTime);
if (code == Keyboard.CODE_OUTPUT_TEXT) { if (code == Keyboard.CODE_OUTPUT_TEXT) {
mListener.onTextInput(key.getOutputText()); mListener.onTextInput(key.getOutputText());
} else if (code != Keyboard.CODE_UNSPECIFIED) { } else if (code != Keyboard.CODE_UNSPECIFIED) {
@ -471,10 +541,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
private static boolean needsToSuppressKeyPreviewPopup(final long eventTime) { private static boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
if (!sShouldHandleGesture) return false; if (!sShouldHandleGesture) return false;
if (sLastBatchInputTime == 0) return false; return sTimeRecorder.needsToSuppressKeyPreviewPopup(eventTime);
final long elapsedTimeAfterTheLastBatchInput = eventTime - sLastBatchInputTime;
return elapsedTimeAfterTheLastBatchInput
< SUPPRESS_KEY_PREVIEW_AFTER_LAST_BATCH_INPUT_DURATION;
} }
private void setPressedKeyGraphics(final Key key, final long eventTime) { private void setPressedKeyGraphics(final Key key, final long eventTime) {
@ -620,7 +687,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
mPointerId, sAggregratedPointers.getPointerSize())); mPointerId, sAggregratedPointers.getPointerSize()));
} }
sInGesture = false; sInGesture = false;
sLastBatchInputTime = eventTime; sTimeRecorder.onEndBatchInput(eventTime);
mListener.onEndBatchInput(sAggregratedPointers); mListener.onEndBatchInput(sAggregratedPointers);
} }
} }
@ -698,7 +765,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
sGestureFirstDownTime = eventTime; sGestureFirstDownTime = eventTime;
} }
mGestureStrokeWithPreviewPoints.onDownEvent(x, y, eventTime, sGestureFirstDownTime, mGestureStrokeWithPreviewPoints.onDownEvent(x, y, eventTime, sGestureFirstDownTime,
sLastLetterTypingUpTime); sTimeRecorder.getLastLetterTypingTime());
} }
} }
@ -939,11 +1006,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return; return;
} }
if (currentKey != null && !currentKey.isRepeatable()) { if (currentKey != null && !currentKey.isRepeatable()) {
detectAndSendKey(currentKey, mKeyX, mKeyY); detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
final int code = currentKey.mCode;
if (Keyboard.isLetterCode(code) && code != Keyboard.CODE_SPACE) {
sLastLetterTypingUpTime = eventTime;
}
} }
} }
@ -994,7 +1057,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
public void onRegisterKey(final Key key) { public void onRegisterKey(final Key key) {
if (key != null) { if (key != null) {
detectAndSendKey(key, key.mX, key.mY); detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis());
mTimerProxy.startTypingStateTimer(key); mTimerProxy.startTypingStateTimer(key);
} }
} }
@ -1020,14 +1083,14 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
} }
} }
private void detectAndSendKey(final Key key, final int x, final int y) { private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
if (key == null) { if (key == null) {
callListenerOnCancelInput(); callListenerOnCancelInput();
return; return;
} }
final int code = key.mCode; final int code = key.mCode;
callListenerOnCodeInput(key, code, x, y); callListenerOnCodeInput(key, code, x, y, eventTime);
callListenerOnRelease(key, code, false); callListenerOnRelease(key, code, false);
} }