aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/keyboard/PointerTracker.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/keyboard/PointerTracker.java')
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java206
1 files changed, 29 insertions, 177 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index ef48da646..291cc8d96 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -19,16 +19,17 @@ package com.android.inputmethod.keyboard;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.SystemClock;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
-import com.android.inputmethod.accessibility.AccessibilityUtils;
+import com.android.inputmethod.keyboard.internal.BogusMoveEventDetector;
+import com.android.inputmethod.keyboard.internal.GestureEnabler;
import com.android.inputmethod.keyboard.internal.GestureStroke;
import com.android.inputmethod.keyboard.internal.GestureStroke.GestureStrokeParams;
import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints;
import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints.GestureStrokePreviewParams;
import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
+import com.android.inputmethod.keyboard.internal.TypingTimeRecorder;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.LatinImeLogger;
@@ -49,12 +50,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
private static final boolean DEBUG_LISTENER = false;
private static boolean DEBUG_MODE = LatinImeLogger.sDBG || DEBUG_EVENT;
- /** True if {@link PointerTracker}s should handle gesture events. */
- private static boolean sShouldHandleGesture = false;
- private static boolean sMainDictionaryAvailable = false;
- private static boolean sGestureHandlingEnabledByInputField = false;
- private static boolean sGestureHandlingEnabledByUser = false;
-
public interface DrawingProxy {
public void invalidateKey(Key key);
public void showKeyPreview(Key key);
@@ -136,6 +131,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
}
+ private static GestureEnabler sGestureEnabler = new GestureEnabler();
+
// Parameters for pointer handling.
private static PointerTrackerParams sParams;
private static GestureStrokeParams sGestureStrokeParams;
@@ -144,9 +141,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
// Move this threshold to resource.
// TODO: Device specific parameter would be better for device specific hack?
private static final float PHANTOM_SUDDEN_MOVE_THRESHOLD = 0.25f; // in keyWidth
- // This hack is applied to certain classes of tablets.
- // See {@link #needsProximateBogusDownMoveUpEventHack(Resources)}.
- private static boolean sNeedsProximateBogusDownMoveUpEventHack;
private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList();
private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue();
@@ -155,10 +149,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
private static DrawingProxy sDrawingProxy;
private static TimerProxy sTimerProxy;
- private static KeyDetector sDefaultKeyDetector;
- private KeyDetector mKeyDetector;
private static KeyboardActionListener sListener = KeyboardActionListener.EMPTY_LISTENER;
+ // The {@link KeyDetector} is set whenever the down event is processed. Also this is updated
+ // when new {@link Keyboard} is set by {@link #setKeyDetector(KeyDetector)}.
+ private KeyDetector mKeyDetector;
private Keyboard mKeyboard;
private int mPhantomSuddenMoveThreshold;
private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
@@ -166,121 +161,12 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
private boolean mIsDetectingGesture = false; // per PointerTracker.
private static boolean sInGesture = false;
private static long sGestureFirstDownTime;
- private static TimeRecorder sTimeRecorder;
+ private static TypingTimeRecorder sTypingTimeRecorder;
private static final InputPointers sAggregatedPointers = new InputPointers(
GestureStroke.DEFAULT_CAPACITY);
private static int sLastRecognitionPointSize = 0; // synchronized using sAggregatedPointers
private static long sLastRecognitionTime = 0; // synchronized using sAggregatedPointers
- static final class BogusMoveEventDetector {
- // Move these thresholds to resource.
- // These thresholds' unit is a diagonal length of a key.
- private static final float BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD = 0.53f;
- private static final float BOGUS_MOVE_RADIUS_THRESHOLD = 1.14f;
-
- private int mAccumulatedDistanceThreshold;
- private int mRadiusThreshold;
-
- // Accumulated distance from actual and artificial down keys.
- /* package */ int mAccumulatedDistanceFromDownKey;
- private int mActualDownX;
- private int mActualDownY;
-
- public void setKeyboardGeometry(final int keyWidth, final int keyHeight) {
- final float keyDiagonal = (float)Math.hypot(keyWidth, keyHeight);
- mAccumulatedDistanceThreshold = (int)(
- keyDiagonal * BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD);
- mRadiusThreshold = (int)(keyDiagonal * BOGUS_MOVE_RADIUS_THRESHOLD);
- }
-
- public void onActualDownEvent(final int x, final int y) {
- mActualDownX = x;
- mActualDownY = y;
- }
-
- public void onDownKey() {
- mAccumulatedDistanceFromDownKey = 0;
- }
-
- public void onMoveKey(final int distance) {
- mAccumulatedDistanceFromDownKey += distance;
- }
-
- public boolean hasTraveledLongDistance(final int x, final int y) {
- final int dx = Math.abs(x - mActualDownX);
- final int dy = Math.abs(y - mActualDownY);
- // A bogus move event should be a horizontal movement. A vertical movement might be
- // a sloppy typing and should be ignored.
- return dx >= dy && mAccumulatedDistanceFromDownKey >= mAccumulatedDistanceThreshold;
- }
-
- /* package */ int getDistanceFromDownEvent(final int x, final int y) {
- return getDistance(x, y, mActualDownX, mActualDownY);
- }
-
- public boolean isCloseToActualDownEvent(final int x, final int y) {
- return getDistanceFromDownEvent(x, y) < mRadiusThreshold;
- }
- }
-
- 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;
- }
-
- public boolean isInFastTyping(final long eventTime) {
- final long elapsedTimeSinceLastLetterTyping = eventTime - mLastLetterTypingTime;
- return elapsedTimeSinceLastLetterTyping < mStaticTimeThresholdAfterFastTyping;
- }
-
- private boolean wasLastInputTyping() {
- return mLastTypingTime >= mLastBatchInputTime;
- }
-
- public void onCodeInput(final int code, final long eventTime) {
- // Record the letter typing time when
- // 1. Letter keys are typed successively without any batch input in between.
- // 2. A letter key is typed within the threshold time since the last any key typing.
- // 3. A non-letter key is typed within the threshold time since the last letter key
- // typing.
- if (Character.isLetter(code)) {
- if (wasLastInputTyping()
- || eventTime - mLastTypingTime < mStaticTimeThresholdAfterFastTyping) {
- mLastLetterTypingTime = eventTime;
- }
- } else {
- if (eventTime - mLastLetterTypingTime < mStaticTimeThresholdAfterFastTyping) {
- // This non-letter typing should be treated as a part of fast typing.
- mLastLetterTypingTime = eventTime;
- }
- }
- mLastTypingTime = eventTime;
- }
-
- public void onEndBatchInput(final long eventTime) {
- mLastBatchInputTime = eventTime;
- }
-
- public long getLastLetterTypingTime() {
- return mLastLetterTypingTime;
- }
-
- public boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
- return !wasLastInputTyping()
- && eventTime - mLastBatchInputTime < mSuppressKeyPreviewAfterBatchInputDuration;
- }
- }
-
// The position and time at which first down event occurred.
private long mDownTime;
private int[] mDownCoordinates = CoordinateUtils.newInstance();
@@ -319,64 +205,33 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
- private static final int SMALL_TABLET_SMALLEST_WIDTH = 600; // dp
- private static final int LARGE_TABLET_SMALLEST_WIDTH = 768; // dp
-
- private static boolean needsProximateBogusDownMoveUpEventHack(final Resources res) {
- // The proximate bogus down move up event hack is needed for a device such like,
- // 1) is large tablet, or 2) is small tablet and the screen density is less than hdpi.
- // Though it seems odd to use screen density as criteria of the quality of the touch
- // screen, the small table that has a less density screen than hdpi most likely has been
- // made with the touch screen that needs the hack.
- final int sw = res.getConfiguration().smallestScreenWidthDp;
- final boolean isLargeTablet = (sw >= LARGE_TABLET_SMALLEST_WIDTH);
- final boolean isSmallTablet =
- (sw >= SMALL_TABLET_SMALLEST_WIDTH && sw < LARGE_TABLET_SMALLEST_WIDTH);
- final int densityDpi = res.getDisplayMetrics().densityDpi;
- final boolean hasLowDensityScreen = (densityDpi < DisplayMetrics.DENSITY_HIGH);
- final boolean needsTheHack = isLargeTablet || (isSmallTablet && hasLowDensityScreen);
- if (DEBUG_MODE) {
- Log.d(TAG, "needsProximateBogusDownMoveUpEventHack=" + needsTheHack
- + " smallestScreenWidthDp=" + sw + " densityDpi=" + densityDpi);
- }
- return needsTheHack;
- }
-
// TODO: Add PointerTrackerFactory singleton and move some class static methods into it.
public static void init(final TypedArray mainKeyboardViewAttr, final TimerProxy timerProxy,
- final DrawingProxy drawingProxy, final KeyDetector defaultKeyDetector) {
+ final DrawingProxy drawingProxy) {
sParams = new PointerTrackerParams(mainKeyboardViewAttr);
sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
sGesturePreviewParams = new GestureStrokePreviewParams(mainKeyboardViewAttr);
- sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
+ sTypingTimeRecorder = new TypingTimeRecorder(
+ sGestureStrokeParams.mStaticTimeThresholdAfterFastTyping,
+ sParams.mSuppressKeyPreviewAfterBatchInputDuration);
final Resources res = mainKeyboardViewAttr.getResources();
sNeedsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
ResourceUtils.getDeviceOverrideValue(
res, R.array.phantom_sudden_move_event_device_list));
- sNeedsProximateBogusDownMoveUpEventHack = needsProximateBogusDownMoveUpEventHack(res);
+ BogusMoveEventDetector.init(res);
sTimerProxy = timerProxy;
sDrawingProxy = drawingProxy;
- sDefaultKeyDetector = defaultKeyDetector;
- }
-
- private static void updateGestureHandlingMode() {
- sShouldHandleGesture = sMainDictionaryAvailable
- && sGestureHandlingEnabledByInputField
- && sGestureHandlingEnabledByUser
- && !AccessibilityUtils.getInstance().isTouchExplorationEnabled();
}
// Note that this method is called from a non-UI thread.
public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
- sMainDictionaryAvailable = mainDictionaryAvailable;
- updateGestureHandlingMode();
+ sGestureEnabler.setMainDictionaryAvailability(mainDictionaryAvailable);
}
public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
- sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser;
- updateGestureHandlingMode();
+ sGestureEnabler.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser);
}
public static PointerTracker getPointerTracker(final int id) {
@@ -410,8 +265,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
tracker.setKeyDetectorInner(keyDetector);
}
final Keyboard keyboard = keyDetector.getKeyboard();
- sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
- updateGestureHandlingMode();
+ sGestureEnabler.setPasswordMode(keyboard.mId.passwordInput());
}
public static void setReleasedKeyGraphicsToAllKeys() {
@@ -434,7 +288,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
mPointerId = id;
mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(
id, sGestureStrokeParams, sGesturePreviewParams);
- setKeyDetectorInner(sDefaultKeyDetector);
}
// Returns true if keyboard has been changed by this callback.
@@ -491,7 +344,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
// Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
if (key.isEnabled() || altersCode) {
- sTimeRecorder.onCodeInput(code, eventTime);
+ sTypingTimeRecorder.onCodeInput(code, eventTime);
if (code == Constants.CODE_OUTPUT_TEXT) {
sListener.onTextInput(key.getOutputText());
} else if (code != Constants.CODE_UNSPECIFIED) {
@@ -617,8 +470,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
private static boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
- if (!sShouldHandleGesture) return false;
- return sTimeRecorder.needsToSuppressKeyPreviewPopup(eventTime);
+ if (!sGestureEnabler.shouldHandleGesture()) return false;
+ return sTypingTimeRecorder.needsToSuppressKeyPreviewPopup(eventTime);
}
private void setPressedKeyGraphics(final Key key, final long eventTime) {
@@ -693,7 +546,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
}
- static int getDistance(final int x1, final int y1, final int x2, final int y2) {
+ private static int getDistance(final int x1, final int y1, final int x2, final int y2) {
return (int)Math.hypot(x1 - x2, y1 - y2);
}
@@ -791,7 +644,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregatedPointers);
if (getActivePointerTrackerCount() == 1) {
sInGesture = false;
- sTimeRecorder.onEndBatchInput(eventTime);
+ sTypingTimeRecorder.onEndBatchInput(eventTime);
sTimerProxy.cancelAllUpdateBatchInputTimers();
if (!mIsTrackingForActionDisabled) {
if (DEBUG_LISTENER) {
@@ -894,7 +747,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
sPointerTrackerQueue.add(this);
onDownEventInternal(x, y, eventTime);
- if (!sShouldHandleGesture) {
+ if (!sGestureEnabler.shouldHandleGesture()) {
return;
}
// A gesture should start only from a non-modifier key. Note that the gesture detection is
@@ -906,7 +759,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
sGestureFirstDownTime = eventTime;
}
mGestureStrokeWithPreviewPoints.onDownEvent(x, y, eventTime, sGestureFirstDownTime,
- sTimeRecorder.getLastLetterTypingTime());
+ sTypingTimeRecorder.getLastLetterTypingTime());
}
}
@@ -995,7 +848,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return;
}
- if (sShouldHandleGesture && me != null) {
+ if (sGestureEnabler.shouldHandleGesture() && me != null) {
// Add historical points to gesture path.
final int pointerIndex = me.findPointerIndex(mPointerId);
final int historicalSize = me.getHistorySize();
@@ -1103,7 +956,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
// HACK: On some devices, quick successive proximate touches may be reported as a bogus
// down-move-up event by touch panel firmware. This hack detects such cases and breaks
// these events into separate up and down events.
- else if (sNeedsProximateBogusDownMoveUpEventHack && sTimeRecorder.isInFastTyping(eventTime)
+ else if (sTypingTimeRecorder.isInFastTyping(eventTime)
&& mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) {
processProximateBogusDownMoveUpEventHack(key, x, y, eventTime, oldKey, lastX, lastY);
}
@@ -1147,7 +1000,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
final Key oldKey = mCurrentKey;
final Key newKey = onMoveKey(x, y);
- if (sShouldHandleGesture) {
+ if (sGestureEnabler.shouldHandleGesture()) {
// Register move event on gesture tracker.
onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, newKey);
if (sInGesture) {
@@ -1320,14 +1173,13 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
return true;
}
- if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedDraggingFinger
- && sTimeRecorder.isInFastTyping(eventTime)
+ if (!mIsAllowedDraggingFinger && sTypingTimeRecorder.isInFastTyping(eventTime)
&& mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
if (DEBUG_MODE) {
final float keyDiagonal = (float)Math.hypot(
mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
final float lengthFromDownRatio =
- mBogusMoveEventDetector.mAccumulatedDistanceFromDownKey / keyDiagonal;
+ mBogusMoveEventDetector.getAccumulatedDistanceFromDownKey() / keyDiagonal;
Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
+ " %.2f key diagonal from virtual down point",
mPointerId, lengthFromDownRatio));