diff options
Diffstat (limited to 'java/src/com/android/inputmethod/keyboard/PointerTracker.java')
-rw-r--r-- | java/src/com/android/inputmethod/keyboard/PointerTracker.java | 591 |
1 files changed, 345 insertions, 246 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index d0f7cb276..0f55607a0 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -27,6 +27,8 @@ import com.android.inputmethod.keyboard.internal.GestureStroke.GestureStrokePara import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.latin.CollectionUtils; +import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.CoordinateUtils; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; @@ -75,10 +77,12 @@ public final class PointerTracker implements PointerTrackerQueue.Element { public TimerProxy getTimerProxy(); } - public interface DrawingProxy extends MoreKeysPanel.Controller { + public interface DrawingProxy { public void invalidateKey(Key key); public void showKeyPreview(PointerTracker tracker); public void dismissKeyPreview(PointerTracker tracker); + public void showSlidingKeyInputPreview(PointerTracker tracker); + public void dismissSlidingKeyInputPreview(); public void showGesturePreviewTrail(PointerTracker tracker, boolean isOldestTracker); } @@ -93,6 +97,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element { public void cancelDoubleTapTimer(); public boolean isInDoubleTapTimeout(); public void cancelKeyTimers(); + public void startUpdateBatchInputTimer(PointerTracker tracker); + public void cancelAllUpdateBatchInputTimers(); public static class Adapter implements TimerProxy { @Override @@ -115,6 +121,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element { public boolean isInDoubleTapTimeout() { return false; } @Override public void cancelKeyTimers() {} + @Override + public void startUpdateBatchInputTimer(PointerTracker tracker) {} + @Override + public void cancelAllUpdateBatchInputTimers() {} } } @@ -156,7 +166,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { private static final boolean sNeedsProximateBogusDownMoveUpEventHack = true; private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList(); - private static PointerTrackerQueue sPointerTrackerQueue; + private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue(); public final int mPointerId; @@ -289,6 +299,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { // The position and time at which first down event occurred. private long mDownTime; + private int[] mDownCoordinates = CoordinateUtils.newInstance(); private long mUpTime; // The current key where this pointer is. @@ -304,11 +315,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element { // true if keyboard layout has been changed. private boolean mKeyboardLayoutHasBeenChanged; - // true if event is already translated to a key action. - private boolean mKeyAlreadyProcessed; + // true if this pointer is no longer tracking touch event. + private boolean mIsTrackingCanceled; - // true if this pointer has been long-pressed and is showing a more keys panel. - private boolean mIsShowingMoreKeysPanel; + // the more keys panel currently being shown. equals null if no panel is active. + private MoreKeysPanel mMoreKeysPanel; // true if this pointer is in a sliding key input. boolean mIsInSlidingKeyInput; @@ -325,13 +336,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints; - public static void init(boolean hasDistinctMultitouch, - boolean needsPhantomSuddenMoveEventHack) { - if (hasDistinctMultitouch) { - sPointerTrackerQueue = new PointerTrackerQueue(); - } else { - sPointerTrackerQueue = null; - } + public static void init(final boolean needsPhantomSuddenMoveEventHack) { sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack; sParams = PointerTrackerParams.DEFAULT; sGestureStrokeParams = GestureStrokeParams.DEFAULT; @@ -375,7 +380,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } public static boolean isAnyInSlidingKeyInput() { - return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false; + return sPointerTrackerQueue.isAnyInSlidingKeyInput(); } public static void setKeyboardActionListener(final KeyboardActionListener listener) { @@ -407,6 +412,17 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } + public static void dismissAllMoreKeysPanels() { + final int trackersSize = sTrackers.size(); + for (int i = 0; i < trackersSize; ++i) { + final PointerTracker tracker = sTrackers.get(i); + if (tracker.isShowingMoreKeysPanel()) { + tracker.mMoreKeysPanel.dismissMoreKeysPanel(); + tracker.mMoreKeysPanel = null; + } + } + } + private PointerTracker(final int id, final KeyEventHandler handler) { if (handler == null) { throw new NullPointerException(); @@ -453,8 +469,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element { final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState(); final int code = altersCode ? key.getAltCode() : primaryCode; if (DEBUG_LISTENER) { - final String output = code == Keyboard.CODE_OUTPUT_TEXT - ? key.getOutputText() : Keyboard.printableCode(code); + final String output = code == Constants.CODE_OUTPUT_TEXT + ? key.getOutputText() : Constants.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")); @@ -469,9 +485,9 @@ 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); - if (code == Keyboard.CODE_OUTPUT_TEXT) { + if (code == Constants.CODE_OUTPUT_TEXT) { mListener.onTextInput(key.getOutputText()); - } else if (code != Keyboard.CODE_UNSPECIFIED) { + } else if (code != Constants.CODE_UNSPECIFIED) { mListener.onCodeInput(code, x, y); } } @@ -487,7 +503,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier(); if (DEBUG_LISTENER) { Log.d(TAG, String.format("[%d] onRelease : %s%s%s%s", mPointerId, - Keyboard.printableCode(primaryCode), + Constants.printableCode(primaryCode), withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "", key.isEnabled() ? "": " disabled")); } @@ -522,7 +538,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { mKeyboard = keyDetector.getKeyboard(); final int keyWidth = mKeyboard.mMostCommonKeyWidth; final int keyHeight = mKeyboard.mMostCommonKeyHeight; - mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth); + mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth, mKeyboard.mOccupiedHeight); final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY); if (newKey != mCurrentKey) { if (mDrawingProxy != null) { @@ -539,6 +555,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element { return mIsInSlidingKeyInput; } + public boolean isInSlidingKeyInputFromModifier() { + return mIsInSlidingKeyInputFromModifier; + } + public Key getKey() { return mCurrentKey; } @@ -641,20 +661,21 @@ public final class PointerTracker implements PointerTrackerQueue.Element { return mGestureStrokeWithPreviewPoints; } - public int getLastX() { - return mLastX; - } - - public int getLastY() { - return mLastY; + public void getLastCoordinates(final int[] outCoords) { + CoordinateUtils.set(outCoords, mLastX, mLastY); } public long getDownTime() { return mDownTime; } + public void getDownCoordinates(final int[] outCoords) { + CoordinateUtils.copy(outCoords, mDownCoordinates); + } + private Key onDownKey(final int x, final int y, final long eventTime) { mDownTime = eventTime; + CoordinateUtils.set(mDownCoordinates, x, y); mBogusMoveEventDetector.onDownKey(); return onMoveToNewKey(onMoveKeyInternal(x, y), x, y); } @@ -682,7 +703,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } private static int getActivePointerTrackerCount() { - return (sPointerTrackerQueue == null) ? 1 : sPointerTrackerQueue.size(); + return sPointerTrackerQueue.size(); + } + + private static boolean isOldestTrackerInQueue(final PointerTracker tracker) { + return sPointerTrackerQueue.getOldestElement() == tracker; } private void mayStartBatchInput(final Key key) { @@ -701,48 +726,76 @@ public final class PointerTracker implements PointerTrackerQueue.Element { sLastRecognitionPointSize = 0; sLastRecognitionTime = 0; mListener.onStartBatchInput(); + dismissAllMoreKeysPanels(); } - final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this; - mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker); + mTimerProxy.cancelLongPressTimer(); + mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this)); + } + + public void updateBatchInputByTimer(final long eventTime) { + final int gestureTime = (int)(eventTime - sGestureFirstDownTime); + mGestureStrokeWithPreviewPoints.duplicateLastPointWith(gestureTime); + updateBatchInput(eventTime); } private void mayUpdateBatchInput(final long eventTime, final Key key) { if (key != null) { - synchronized (sAggregratedPointers) { - final GestureStroke stroke = mGestureStrokeWithPreviewPoints; - stroke.appendIncrementalBatchPoints(sAggregratedPointers); - final int size = sAggregratedPointers.getPointerSize(); - if (size > sLastRecognitionPointSize - && stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) { - sLastRecognitionPointSize = size; - sLastRecognitionTime = eventTime; - if (DEBUG_LISTENER) { - Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d", - mPointerId, size)); - } - mListener.onUpdateBatchInput(sAggregratedPointers); + updateBatchInput(eventTime); + } + if (mIsTrackingCanceled) { + return; + } + mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this)); + } + + private void updateBatchInput(final long eventTime) { + synchronized (sAggregratedPointers) { + final GestureStroke stroke = mGestureStrokeWithPreviewPoints; + stroke.appendIncrementalBatchPoints(sAggregratedPointers); + final int size = sAggregratedPointers.getPointerSize(); + if (size > sLastRecognitionPointSize + && stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) { + sLastRecognitionPointSize = size; + sLastRecognitionTime = eventTime; + if (DEBUG_LISTENER) { + Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d", mPointerId, + size)); } + mTimerProxy.startUpdateBatchInputTimer(this); + mListener.onUpdateBatchInput(sAggregratedPointers); } } - final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this; - mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker); } private void mayEndBatchInput(final long eventTime) { synchronized (sAggregratedPointers) { mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers); if (getActivePointerTrackerCount() == 1) { - if (DEBUG_LISTENER) { - Log.d(TAG, String.format("[%d] onEndBatchInput : batchPoints=%d", - mPointerId, sAggregratedPointers.getPointerSize())); - } sInGesture = false; sTimeRecorder.onEndBatchInput(eventTime); - mListener.onEndBatchInput(sAggregratedPointers); + mTimerProxy.cancelAllUpdateBatchInputTimers(); + if (!mIsTrackingCanceled) { + if (DEBUG_LISTENER) { + Log.d(TAG, String.format("[%d] onEndBatchInput : batchPoints=%d", + mPointerId, sAggregratedPointers.getPointerSize())); + } + mListener.onEndBatchInput(sAggregratedPointers); + } } } - final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this; - mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker); + if (mIsTrackingCanceled) { + return; + } + mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this)); + } + + private void cancelBatchInput() { + sPointerTrackerQueue.cancelAllPointerTracker(); + sInGesture = false; + if (DEBUG_LISTENER) { + Log.d(TAG, String.format("[%d] onCancelBatchInput", mPointerId)); + } + mListener.onCancelBatchInput(); } public void processMotionEvent(final int action, final int x, final int y, final long eventTime, @@ -770,7 +823,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (DEBUG_EVENT) { printTouchEvent("onDownEvent:", x, y, eventTime); } - mDrawingProxy = handler.getDrawingProxy(); mTimerProxy = handler.getTimerProxy(); setKeyboardActionListener(handler.getKeyboardActionListener()); @@ -787,29 +839,26 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance); } - mKeyAlreadyProcessed = true; + cancelTracking(); return; } } final Key key = getKeyOn(x, y); mBogusMoveEventDetector.onActualDownEvent(x, y); - final PointerTrackerQueue queue = sPointerTrackerQueue; - if (queue != null) { - if (key != null && key.isModifier()) { - // Before processing a down event of modifier key, all pointers already being - // tracked should be released. - queue.releaseAllPointers(eventTime); - } - queue.add(this); + if (key != null && key.isModifier()) { + // Before processing a down event of modifier key, all pointers already being + // tracked should be released. + sPointerTrackerQueue.releaseAllPointers(eventTime); } + sPointerTrackerQueue.add(this); onDownEventInternal(x, y, eventTime); if (!sShouldHandleGesture) { return; } - // A gesture should start only from the letter key. + // A gesture should start only from a non-modifier key. mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard() - && !mIsShowingMoreKeysPanel && key != null && Keyboard.isLetterCode(key.mCode); + && key != null && !key.isModifier(); if (mIsDetectingGesture) { if (getActivePointerTrackerCount() == 1) { sGestureFirstDownTime = eventTime; @@ -819,6 +868,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } + private boolean isShowingMoreKeysPanel() { + return (mMoreKeysPanel != null); + } + 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 @@ -827,7 +880,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { || (key != null && key.isModifier()) || mKeyDetector.alwaysAllowsSlidingInput(); mKeyboardLayoutHasBeenChanged = false; - mKeyAlreadyProcessed = false; + mIsTrackingCanceled = false; resetSlidingKeyInput(); if (key != null) { // This onPress call may have changed keyboard layout. Those cases are detected at @@ -853,13 +906,24 @@ public final class PointerTracker implements PointerTrackerQueue.Element { private void resetSlidingKeyInput() { mIsInSlidingKeyInput = false; mIsInSlidingKeyInputFromModifier = false; + mDrawingProxy.dismissSlidingKeyInputPreview(); } private void onGestureMoveEvent(final int x, final int y, final long eventTime, final boolean isMajorEvent, final Key key) { final int gestureTime = (int)(eventTime - sGestureFirstDownTime); if (mIsDetectingGesture) { - mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isMajorEvent); + final boolean onValidArea = mGestureStrokeWithPreviewPoints.addPointOnKeyboard( + x, y, gestureTime, isMajorEvent); + if (!onValidArea) { + cancelBatchInput(); + return; + } + // If the MoreKeysPanel is showing then do not attempt to enter gesture mode. However, + // the gestured touch points are still being recorded in case the panel is dismissed. + if (isShowingMoreKeysPanel()) { + return; + } mayStartBatchInput(key); if (sInGesture) { mayUpdateBatchInput(eventTime, key); @@ -871,10 +935,16 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (DEBUG_MOVE_EVENT) { printTouchEvent("onMoveEvent:", x, y, eventTime); } - if (mKeyAlreadyProcessed) { + if (mIsTrackingCanceled) { return; } + if (isShowingMoreKeysPanel()) { + final int translatedX = mMoreKeysPanel.translateX(x); + final int translatedY = mMoreKeysPanel.translateY(y); + mMoreKeysPanel.onMoveEvent(translatedX, translatedY, mPointerId, eventTime); + } + if (sShouldHandleGesture && me != null) { // Add historical points to gesture path. final int pointerIndex = me.findPointerIndex(mPointerId); @@ -888,141 +958,160 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } + if (isShowingMoreKeysPanel()) { + // Do not handle sliding keys (or show key pop-ups) when the MoreKeysPanel is visible. + return; + } onMoveEventInternal(x, y, eventTime); } + private void processSlidingKeyInput(final Key newKey, final int x, final int y, + final long eventTime) { + // This onPress call may have changed keyboard layout. Those cases are detected + // at {@link #setKeyboard}. In those cases, we should update key according + // to the new keyboard layout. + Key key = newKey; + if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) { + key = onMoveKey(x, y); + } + onMoveToNewKey(key, x, y); + startLongPressTimer(key); + setPressedKeyGraphics(key, eventTime); + } + + private void processPhantomSuddenMoveHack(final Key key, final int x, final int y, + final long eventTime, final Key oldKey, final int lastX, final int lastY) { + if (DEBUG_MODE) { + Log.w(TAG, String.format("[%d] onMoveEvent:" + + " phantom sudden move event (distance=%d) is translated to " + + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId, + getDistance(x, y, lastX, lastY), + lastX, lastY, Constants.printableCode(oldKey.mCode), + x, y, Constants.printableCode(key.mCode))); + } + // TODO: This should be moved to outside of this nested if-clause? + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY); + } + onUpEventInternal(x, y, eventTime); + onDownEventInternal(x, y, eventTime); + } + + private void processProximateBogusDownMoveUpEventHack(final Key key, final int x, final int y, + final long eventTime, final Key oldKey, final int lastX, final int lastY) { + if (DEBUG_MODE) { + final float keyDiagonal = (float)Math.hypot( + mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight); + final float radiusRatio = + mBogusMoveEventDetector.getDistanceFromDownEvent(x, y) + / keyDiagonal; + Log.w(TAG, String.format("[%d] onMoveEvent:" + + " bogus down-move-up event (raidus=%.2f key diagonal) is " + + " translated to up[%d,%d,%s]/down[%d,%d,%s] events", + mPointerId, radiusRatio, + lastX, lastY, Constants.printableCode(oldKey.mCode), + x, y, Constants.printableCode(key.mCode))); + } + onUpEventInternal(x, y, eventTime); + onDownEventInternal(x, y, eventTime); + } + + private void processSildeOutFromOldKey(final Key oldKey) { + setReleasedKeyGraphics(oldKey); + callListenerOnRelease(oldKey, oldKey.mCode, true); + startSlidingKeyInput(oldKey); + mTimerProxy.cancelKeyTimers(); + } + + private void slideFromOldKeyToNewKey(final Key key, final int x, final int y, + final long eventTime, final Key oldKey, final int lastX, final int lastY) { + // The pointer has been slid in to the new key from the previous key, we must call + // onRelease() first to notify that the previous key has been released, then call + // onPress() to notify that the new key is being pressed. + processSildeOutFromOldKey(oldKey); + startRepeatKey(key); + if (mIsAllowedSlidingKeyInput) { + processSlidingKeyInput(key, x, y, eventTime); + } + // HACK: On some devices, quick successive touches may be reported as a sudden move by + // touch panel firmware. This hack detects such cases and translates the move event to + // successive up and down events. + // TODO: Should find a way to balance gesture detection and this hack. + else if (sNeedsPhantomSuddenMoveEventHack + && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) { + processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY); + } + // 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) + && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) { + processProximateBogusDownMoveUpEventHack(key, x, y, eventTime, oldKey, lastX, lastY); + } + // HACK: If there are currently multiple touches, register the key even if the finger + // slides off the key. This defends against noise from some touch panels when there are + // close multiple touches. + // Caveat: When in chording input mode with a modifier key, we don't use this hack. + else if (getActivePointerTrackerCount() > 1 + && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) { + if (DEBUG_MODE) { + Log.w(TAG, String.format("[%d] onMoveEvent:" + + " detected sliding finger while multi touching", mPointerId)); + } + onUpEvent(x, y, eventTime); + cancelTracking(); + setReleasedKeyGraphics(oldKey); + } else { + if (!mIsDetectingGesture) { + cancelTracking(); + } + setReleasedKeyGraphics(oldKey); + } + } + + private void slideOutFromOldKey(final Key oldKey, final int x, final int y) { + // The pointer has been slid out from the previous key, we must call onRelease() to + // notify that the previous key has been released. + processSildeOutFromOldKey(oldKey); + if (mIsAllowedSlidingKeyInput) { + onMoveToNewKey(null, x, y); + } else { + if (!mIsDetectingGesture) { + cancelTracking(); + } + } + } + private void onMoveEventInternal(final int x, final int y, final long eventTime) { final int lastX = mLastX; final int lastY = mLastY; final Key oldKey = mCurrentKey; - Key key = onMoveKey(x, y); + final Key newKey = onMoveKey(x, y); if (sShouldHandleGesture) { // Register move event on gesture tracker. - onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, key); + onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, newKey); if (sInGesture) { - mTimerProxy.cancelLongPressTimer(); mCurrentKey = null; setReleasedKeyGraphics(oldKey); return; } } - if (key != null) { - if (oldKey == null) { + if (newKey != null) { + if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) { + slideFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY); + } else if (oldKey == null) { // The pointer has been slid in to the new key, but the finger was not on any keys. // In this case, we must call onPress() to notify that the new key is being pressed. - // This onPress call may have changed keyboard layout. Those cases are detected at - // {@link #setKeyboard}. In those cases, we should update key according to the - // new keyboard layout. - if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) { - key = onMoveKey(x, y); - } - onMoveToNewKey(key, x, y); - startLongPressTimer(key); - setPressedKeyGraphics(key, eventTime); - } else if (isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, key)) { - // The pointer has been slid in to the new key from the previous key, we must call - // onRelease() first to notify that the previous key has been released, then call - // onPress() to notify that the new key is being pressed. - setReleasedKeyGraphics(oldKey); - callListenerOnRelease(oldKey, oldKey.mCode, true); - startSlidingKeyInput(oldKey); - mTimerProxy.cancelKeyTimers(); - startRepeatKey(key); - if (mIsAllowedSlidingKeyInput) { - // This onPress call may have changed keyboard layout. Those cases are detected - // at {@link #setKeyboard}. In those cases, we should update key according - // to the new keyboard layout. - if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) { - key = onMoveKey(x, y); - } - onMoveToNewKey(key, x, y); - startLongPressTimer(key); - setPressedKeyGraphics(key, eventTime); - } else { - // HACK: On some devices, quick successive touches may be reported as a sudden - // move by touch panel firmware. This hack detects such cases and translates the - // move event to successive up and down events. - // TODO: Should find a way to balance gesture detection and this hack. - if (sNeedsPhantomSuddenMoveEventHack - && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) { - if (DEBUG_MODE) { - Log.w(TAG, String.format("[%d] onMoveEvent:" - + " phantom sudden move event (distance=%d) is translated to " - + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId, - getDistance(x, y, lastX, lastY), - lastX, lastY, Keyboard.printableCode(oldKey.mCode), - x, y, Keyboard.printableCode(key.mCode))); - } - // TODO: This should be moved to outside of this nested if-clause? - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY); - } - onUpEventInternal(eventTime); - onDownEventInternal(x, y, eventTime); - } - // 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) - && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) { - if (DEBUG_MODE) { - final float keyDiagonal = (float)Math.hypot( - mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight); - final float radiusRatio = - mBogusMoveEventDetector.getDistanceFromDownEvent(x, y) - / keyDiagonal; - Log.w(TAG, String.format("[%d] onMoveEvent:" - + " bogus down-move-up event (raidus=%.2f key diagonal) is " - + " translated to up[%d,%d,%s]/down[%d,%d,%s] events", - mPointerId, radiusRatio, - lastX, lastY, Keyboard.printableCode(oldKey.mCode), - x, y, Keyboard.printableCode(key.mCode))); - } - onUpEventInternal(eventTime); - onDownEventInternal(x, y, eventTime); - } else { - // HACK: If there are currently multiple touches, register the key even if - // the finger slides off the key. This defends against noise from some - // touch panels when there are close multiple touches. - // Caveat: When in chording input mode with a modifier key, we don't use - // this hack. - if (getActivePointerTrackerCount() > 1 && sPointerTrackerQueue != null - && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) { - if (DEBUG_MODE) { - Log.w(TAG, String.format("[%d] onMoveEvent:" - + " detected sliding finger while multi touching", - mPointerId)); - } - onUpEvent(x, y, eventTime); - mKeyAlreadyProcessed = true; - } - if (!mIsDetectingGesture) { - mKeyAlreadyProcessed = true; - } - setReleasedKeyGraphics(oldKey); - } - } + processSlidingKeyInput(newKey, x, y, eventTime); } - } else { - if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, key)) { - // The pointer has been slid out from the previous key, we must call onRelease() to - // notify that the previous key has been released. - setReleasedKeyGraphics(oldKey); - callListenerOnRelease(oldKey, oldKey.mCode, true); - startSlidingKeyInput(oldKey); - mTimerProxy.cancelLongPressTimer(); - if (mIsAllowedSlidingKeyInput) { - onMoveToNewKey(key, x, y); - } else { - if (!mIsDetectingGesture) { - mKeyAlreadyProcessed = true; - } - } + } else { // newKey == null + if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) { + slideOutFromOldKey(oldKey, x, y); } } + mDrawingProxy.showSlidingKeyInputPreview(this); } public void onUpEvent(final int x, final int y, final long eventTime) { @@ -1030,22 +1119,17 @@ public final class PointerTracker implements PointerTrackerQueue.Element { printTouchEvent("onUpEvent :", x, y, eventTime); } - final PointerTrackerQueue queue = sPointerTrackerQueue; - if (queue != null) { - if (!sInGesture) { - if (mCurrentKey != null && mCurrentKey.isModifier()) { - // Before processing an up event of modifier key, all pointers already being - // tracked should be released. - queue.releaseAllPointersExcept(this, eventTime); - } else { - queue.releaseAllPointersOlderThan(this, eventTime); - } + if (!sInGesture) { + if (mCurrentKey != null && mCurrentKey.isModifier()) { + // Before processing an up event of modifier key, all pointers already being + // tracked should be released. + sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime); + } else { + sPointerTrackerQueue.releaseAllPointersOlderThan(this, eventTime); } } - onUpEventInternal(eventTime); - if (queue != null) { - queue.remove(this); - } + onUpEventInternal(x, y, eventTime); + sPointerTrackerQueue.remove(this); } // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event. @@ -1054,13 +1138,16 @@ public final class PointerTracker implements PointerTrackerQueue.Element { @Override public void onPhantomUpEvent(final long eventTime) { if (DEBUG_EVENT) { - printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime); + printTouchEvent("onPhntEvent:", mLastX, mLastY, eventTime); } - onUpEventInternal(eventTime); - mKeyAlreadyProcessed = true; + if (isShowingMoreKeysPanel()) { + return; + } + onUpEventInternal(mLastX, mLastY, eventTime); + cancelTracking(); } - private void onUpEventInternal(final long eventTime) { + private void onUpEventInternal(final int x, final int y, final long eventTime) { mTimerProxy.cancelKeyTimers(); resetSlidingKeyInput(); mIsDetectingGesture = false; @@ -1068,9 +1155,16 @@ public final class PointerTracker implements PointerTrackerQueue.Element { mCurrentKey = null; // Release the last pressed key. setReleasedKeyGraphics(currentKey); - if (mIsShowingMoreKeysPanel) { - mDrawingProxy.dismissMoreKeysPanel(); - mIsShowingMoreKeysPanel = false; + + if (isShowingMoreKeysPanel()) { + if (!mIsTrackingCanceled) { + final int translatedX = mMoreKeysPanel.translateX(x); + final int translatedY = mMoreKeysPanel.translateY(y); + mMoreKeysPanel.onUpEvent(translatedX, translatedY, mPointerId, eventTime); + } + mMoreKeysPanel.dismissMoreKeysPanel(); + mMoreKeysPanel = null; + return; } if (sInGesture) { @@ -1081,7 +1175,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { return; } - if (mKeyAlreadyProcessed) { + if (mIsTrackingCanceled) { return; } if (currentKey != null && !currentKey.isRepeatable()) { @@ -1089,19 +1183,24 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } - public void onShowMoreKeysPanel(final int x, final int y, final KeyEventHandler handler) { - onLongPressed(); - mIsShowingMoreKeysPanel = true; - onDownEvent(x, y, SystemClock.uptimeMillis(), handler); + public void onShowMoreKeysPanel(final int translatedX, final int translatedY, + final MoreKeysPanel panel) { + setReleasedKeyGraphics(mCurrentKey); + final long eventTime = SystemClock.uptimeMillis(); + mMoreKeysPanel = panel; + mMoreKeysPanel.onDownEvent(translatedX, translatedY, mPointerId, eventTime); + } + + @Override + public void cancelTracking() { + mIsTrackingCanceled = true; } public void onLongPressed() { - mKeyAlreadyProcessed = true; + resetSlidingKeyInput(); + cancelTracking(); setReleasedKeyGraphics(mCurrentKey); - final PointerTrackerQueue queue = sPointerTrackerQueue; - if (queue != null) { - queue.remove(this); - } + sPointerTrackerQueue.remove(this); } public void onCancelEvent(final int x, final int y, final long eventTime) { @@ -1109,11 +1208,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element { printTouchEvent("onCancelEvt:", x, y, eventTime); } - final PointerTrackerQueue queue = sPointerTrackerQueue; - if (queue != null) { - queue.releaseAllPointersExcept(this, eventTime); - queue.remove(this); + if (sInGesture) { + cancelBatchInput(); } + sPointerTrackerQueue.cancelAllPointerTracker(); + sPointerTrackerQueue.releaseAllPointers(eventTime); onCancelEventInternal(); } @@ -1121,9 +1220,9 @@ public final class PointerTracker implements PointerTrackerQueue.Element { mTimerProxy.cancelKeyTimers(); setReleasedKeyGraphics(mCurrentKey); resetSlidingKeyInput(); - if (mIsShowingMoreKeysPanel) { - mDrawingProxy.dismissMoreKeysPanel(); - mIsShowingMoreKeysPanel = false; + if (isShowingMoreKeysPanel()) { + mMoreKeysPanel.dismissMoreKeysPanel(); + mMoreKeysPanel = null; } } @@ -1149,38 +1248,38 @@ public final class PointerTracker implements PointerTrackerQueue.Element { final Key curKey = mCurrentKey; if (newKey == curKey) { return false; - } else if (curKey != null) { - final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared( - mIsInSlidingKeyInputFromModifier); - final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y); - if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) { - if (DEBUG_MODE) { - final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared) - / mKeyboard.mMostCommonKeyWidth; - Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:" - +" %.2f key width from key edge", - mPointerId, distanceToEdgeRatio)); - } - return true; + } + if (curKey == null /* && newKey != null */) { + return true; + } + // Here curKey points to the different key from newKey. + final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared( + mIsInSlidingKeyInputFromModifier); + final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y); + if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) { + if (DEBUG_MODE) { + final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared) + / mKeyboard.mMostCommonKeyWidth; + Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:" + +" %.2f key width from key edge", mPointerId, distanceToEdgeRatio)); } - if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput - && sTimeRecorder.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; - Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:" - + " %.2f key diagonal from virtual down point", - mPointerId, lengthFromDownRatio)); - } - return true; + return true; + } + if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput + && sTimeRecorder.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; + Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:" + + " %.2f key diagonal from virtual down point", + mPointerId, lengthFromDownRatio)); } - return false; - } else { // curKey == null && newKey != null return true; } + return false; } private void startLongPressTimer(final Key key) { @@ -1205,6 +1304,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element { final Key key = mKeyDetector.detectHitKey(x, y); final String code = KeyDetector.printableCode(key); Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId, - (mKeyAlreadyProcessed ? "-" : " "), title, x, y, eventTime, code)); + (mIsTrackingCanceled ? "-" : " "), title, x, y, eventTime, code)); } } |