diff options
Diffstat (limited to 'java/src/com/android/inputmethod/keyboard/PointerTracker.java')
-rw-r--r-- | java/src/com/android/inputmethod/keyboard/PointerTracker.java | 206 |
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)); |