Consolidate GestureTracker into PointerTracker

Change-Id: Ib28fae10493a9142ba4dff6cf57f52c59766b209
main
Tadashi G. Takaoka 2012-07-18 17:56:30 +09:00
parent 10102f02af
commit 9580c467f9
3 changed files with 112 additions and 196 deletions

View File

@ -23,7 +23,6 @@ import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import com.android.inputmethod.keyboard.internal.GestureStroke; import com.android.inputmethod.keyboard.internal.GestureStroke;
import com.android.inputmethod.keyboard.internal.GestureTracker;
import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.LatinImeLogger;
@ -39,6 +38,10 @@ public class PointerTracker {
private static final boolean DEBUG_LISTENER = false; private static final boolean DEBUG_LISTENER = false;
private static boolean DEBUG_MODE = LatinImeLogger.sDBG; private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
// TODO: There should be an option to turn on/off the gesture input.
private static final boolean GESTURE_ON = true;
private static final int MIN_RECOGNITION_TIME = 100; // 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.
@ -128,6 +131,13 @@ public class PointerTracker {
private int mKeyQuarterWidthSquared; private int mKeyQuarterWidthSquared;
private final TextView mKeyPreviewText; private final TextView mKeyPreviewText;
private boolean mIsAlphabetKeyboard;
private boolean mIsPossibleGesture = false;
private boolean mInGesture = false;
private int mLastRecognitionPointSize = 0;
private long mLastRecognitionTime = 0;
// 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;
@ -164,9 +174,6 @@ public class PointerTracker {
private static final KeyboardActionListener EMPTY_LISTENER = private static final KeyboardActionListener EMPTY_LISTENER =
new KeyboardActionListener.Adapter(); new KeyboardActionListener.Adapter();
// Gesture tracker singleton instance
private static final GestureTracker sGestureTracker = GestureTracker.getInstance();
private final GestureStroke mGestureStroke; private final GestureStroke mGestureStroke;
public static void init(boolean hasDistinctMultitouch, public static void init(boolean hasDistinctMultitouch,
@ -207,7 +214,6 @@ public class PointerTracker {
for (final PointerTracker tracker : sTrackers) { for (final PointerTracker tracker : sTrackers) {
tracker.mListener = listener; tracker.mListener = listener;
} }
GestureTracker.init(listener);
} }
public static void setKeyDetector(KeyDetector keyDetector) { public static void setKeyDetector(KeyDetector keyDetector) {
@ -216,7 +222,6 @@ public class PointerTracker {
// Mark that keyboard layout has been changed. // Mark that keyboard layout has been changed.
tracker.mKeyboardLayoutHasBeenChanged = true; tracker.mKeyboardLayoutHasBeenChanged = true;
} }
sGestureTracker.setKeyboard(keyDetector.getKeyboard());
} }
public static void dismissAllKeyPreviews() { public static void dismissAllKeyPreviews() {
@ -226,35 +231,35 @@ public class PointerTracker {
} }
} }
// The working and returning object of the following methods, // TODO: To handle multi-touch gestures we may want to move this method to
// {@link #getIncrementalBatchPoints()} and {@link #getAllBatchPoints()}. // {@link PointerTrackerQueue}.
private static final InputPointers mAggregatedPointers = new InputPointers(); private static InputPointers getIncrementalBatchPoints() {
final InputPointers pointers = new InputPointers();
// TODO: This method is called only from GestureTracker and should address the thread-safty // TODO: Add a default capacity parameter for the InputPointers' constructor.
// issue soon. // TODO: Avoid creating a new instance here?
public static InputPointers getIncrementalBatchPoints() {
final InputPointers pointers = mAggregatedPointers;
pointers.reset();
for (final PointerTracker tracker : sTrackers) { for (final PointerTracker tracker : sTrackers) {
tracker.getGestureStroke().appendIncrementalBatchPoints(pointers); tracker.mGestureStroke.appendIncrementalBatchPoints(pointers);
} }
return pointers; return pointers;
} }
// TODO: This method is called only from GestureTracker and should address the thread-safety // TODO: To handle multi-touch gestures we may want to move this method to
// issue soon. // {@link PointerTrackerQueue}.
public static InputPointers getAllBatchPoints() { private static InputPointers getAllBatchPoints() {
final InputPointers pointers = mAggregatedPointers; // TODO: Add a default capacity parameter for the InputPointers' constructor.
pointers.reset(); // TODO: Avoid creating a new instance here?
final InputPointers pointers = new InputPointers();
for (final PointerTracker tracker : sTrackers) { for (final PointerTracker tracker : sTrackers) {
tracker.getGestureStroke().appendAllBatchPoints(pointers); tracker.mGestureStroke.appendAllBatchPoints(pointers);
} }
return pointers; return pointers;
} }
// TODO: To handle multi-touch gestures we may want to move this method to
// {@link PointerTrackerQueue}.
public static void clearBatchInputPoints() { public static void clearBatchInputPoints() {
for (final PointerTracker tracker : sTrackers) { for (final PointerTracker tracker : sTrackers) {
tracker.getGestureStroke().reset(); tracker.mGestureStroke.reset();
} }
} }
@ -274,13 +279,9 @@ public class PointerTracker {
return mKeyPreviewText; return mKeyPreviewText;
} }
public GestureStroke getGestureStroke() {
return mGestureStroke;
}
// Returns true if keyboard has been changed by this callback. // Returns true if keyboard has been changed by this callback.
private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) { private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) {
if (sGestureTracker.isInGesture()) { if (mInGesture) {
return false; return false;
} }
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier(); final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
@ -336,7 +337,7 @@ public class PointerTracker {
// 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 callListenerOnRelease(Key key, int primaryCode, boolean withSliding) { private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) {
if (sGestureTracker.isInGesture()) { if (mInGesture) {
return; return;
} }
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier(); final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
@ -369,6 +370,7 @@ public class PointerTracker {
private void setKeyDetectorInner(KeyDetector keyDetector) { private void setKeyDetectorInner(KeyDetector keyDetector) {
mKeyDetector = keyDetector; mKeyDetector = keyDetector;
mKeyboard = keyDetector.getKeyboard(); mKeyboard = keyDetector.getKeyboard();
mIsAlphabetKeyboard = mKeyboard.mId.isAlphabetKeyboard();
mGestureStroke.setGestureSampleLength( mGestureStroke.setGestureSampleLength(
mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight); mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY); final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
@ -441,7 +443,7 @@ public class PointerTracker {
return; return;
} }
if (!key.noKeyPreview() && !sGestureTracker.isInGesture()) { if (!key.noKeyPreview() && !mInGesture) {
mDrawingProxy.showKeyPreview(this); mDrawingProxy.showKeyPreview(this);
} }
updatePressKeyGraphics(key); updatePressKeyGraphics(key);
@ -512,6 +514,45 @@ public class PointerTracker {
return newKey; return newKey;
} }
private void startBatchInput() {
if (DEBUG_LISTENER) {
Log.d(TAG, "onStartBatchInput");
}
mInGesture = true;
mListener.onStartBatchInput();
}
private void updateBatchInput(InputPointers batchPoints) {
if (DEBUG_LISTENER) {
Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
}
mListener.onUpdateBatchInput(batchPoints);
}
private void endBatchInput(InputPointers batchPoints) {
if (DEBUG_LISTENER) {
Log.d(TAG, "onEndBatchInput: batchPoints=" + batchPoints.getPointerSize());
}
mListener.onEndBatchInput(batchPoints);
mInGesture = false;
clearBatchInputPoints();
}
private void abortBatchInput() {
mIsPossibleGesture = false;
mInGesture = false;
}
private boolean updateBatchInputRecognitionState(long eventTime, int size) {
if (size > mLastRecognitionPointSize
&& eventTime > mLastRecognitionTime + MIN_RECOGNITION_TIME) {
mLastRecognitionPointSize = size;
mLastRecognitionTime = eventTime;
return true;
}
return false;
}
public void processMotionEvent(int action, int x, int y, long eventTime, public void processMotionEvent(int action, int x, int y, long eventTime,
KeyEventHandler handler) { KeyEventHandler handler) {
switch (action) { switch (action) {
@ -570,7 +611,13 @@ public class PointerTracker {
} }
onDownEventInternal(x, y, eventTime); onDownEventInternal(x, y, eventTime);
if (queue != null && queue.size() == 1) { if (queue != null && queue.size() == 1) {
sGestureTracker.onDownEvent(this, x, y, eventTime, key); mIsPossibleGesture = false;
// A gesture should start only from the letter key.
if (GESTURE_ON && mIsAlphabetKeyboard && key != null
&& Keyboard.isLetterCode(key.mCode)) {
mIsPossibleGesture = true;
mGestureStroke.addPoint(x, y, 0, false);
}
} }
} }
@ -606,6 +653,25 @@ public class PointerTracker {
mIsInSlidingKeyInput = true; mIsInSlidingKeyInput = true;
} }
private void onGestureMoveEvent(PointerTracker tracker, int x, int y, long eventTime,
boolean isHistorical, Key key) {
final int gestureTime = (int)(eventTime - tracker.getDownTime());
if (GESTURE_ON && mIsPossibleGesture) {
final GestureStroke stroke = mGestureStroke;
stroke.addPoint(x, y, gestureTime, isHistorical);
if (!mInGesture && stroke.isStartOfAGesture(gestureTime)) {
startBatchInput();
}
}
if (key != null && mInGesture) {
final InputPointers batchPoints = getIncrementalBatchPoints();
if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
updateBatchInput(batchPoints);
}
}
}
public void onMoveEvent(int x, int y, long eventTime, MotionEvent me) { public void onMoveEvent(int x, int y, long eventTime, MotionEvent me) {
if (DEBUG_MOVE_EVENT) if (DEBUG_MOVE_EVENT)
printTouchEvent("onMoveEvent:", x, y, eventTime); printTouchEvent("onMoveEvent:", x, y, eventTime);
@ -620,7 +686,7 @@ public class PointerTracker {
final int historicalX = (int)me.getHistoricalX(pointerIndex, h); final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
final int historicalY = (int)me.getHistoricalY(pointerIndex, h); final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
final long historicalTime = me.getHistoricalEventTime(h); final long historicalTime = me.getHistoricalEventTime(h);
sGestureTracker.onMoveEvent(this, historicalX, historicalY, historicalTime, onGestureMoveEvent(this, historicalX, historicalY, historicalTime,
true /* isHistorical */, null); true /* isHistorical */, null);
} }
} }
@ -631,8 +697,8 @@ public class PointerTracker {
Key key = onMoveKey(x, y); Key key = onMoveKey(x, y);
// Register move event on gesture tracker. // Register move event on gesture tracker.
sGestureTracker.onMoveEvent(this, x, y, eventTime, false, key); onGestureMoveEvent(this, x, y, eventTime, false /* isHistorical */, key);
if (sGestureTracker.isInGesture()) { if (mInGesture) {
mIgnoreModifierKey = true; mIgnoreModifierKey = true;
mTimerProxy.cancelLongPressTimer(); mTimerProxy.cancelLongPressTimer();
mIsInSlidingKeyInput = true; mIsInSlidingKeyInput = true;
@ -729,7 +795,7 @@ public class PointerTracker {
final PointerTrackerQueue queue = sPointerTrackerQueue; final PointerTrackerQueue queue = sPointerTrackerQueue;
if (queue != null) { if (queue != null) {
if (!sGestureTracker.isInGesture()) { if (!mInGesture) {
if (mCurrentKey != null && mCurrentKey.isModifier()) { if (mCurrentKey != null && mCurrentKey.isModifier()) {
// Before processing an up event of modifier key, all pointers already being // Before processing an up event of modifier key, all pointers already being
// tracked should be released. // tracked should be released.
@ -763,21 +829,16 @@ public class PointerTracker {
mIsShowingMoreKeysPanel = false; mIsShowingMoreKeysPanel = false;
} }
if (sGestureTracker.isInGesture()) { if (mInGesture) {
// Register up event on gesture tracker. // Register up event on gesture tracker.
sGestureTracker.onUpEvent(this, x, y, eventTime); // TODO: Figure out how to deal with multiple fingers that are in gesture, sliding,
if (!sPointerTrackerQueue.isAnyInSlidingKeyInput()) { // and/or tapping mode?
// TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code. endBatchInput(getAllBatchPoints());
sGestureTracker.endBatchInput();
}
if (mCurrentKey != null) { if (mCurrentKey != null) {
callListenerOnRelease(mCurrentKey, mCurrentKey.mCode, true); callListenerOnRelease(mCurrentKey, mCurrentKey.mCode, true);
mCurrentKey = null;
} }
mCurrentKey = null;
return; return;
} else {
// TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code.
sGestureTracker.endBatchInput();
} }
if (mKeyAlreadyProcessed) if (mKeyAlreadyProcessed)
@ -791,8 +852,7 @@ public class PointerTracker {
onLongPressed(); onLongPressed();
onDownEvent(x, y, SystemClock.uptimeMillis(), handler); onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
mIsShowingMoreKeysPanel = true; mIsShowingMoreKeysPanel = true;
// TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code. abortBatchInput();
sGestureTracker.abortBatchInput();
} }
public void onLongPressed() { public void onLongPressed() {
@ -827,7 +887,7 @@ public class PointerTracker {
} }
private void startRepeatKey(Key key) { private void startRepeatKey(Key key) {
if (key != null && key.isRepeatable() && !sGestureTracker.isInGesture()) { if (key != null && key.isRepeatable() && !mInGesture) {
onRegisterKey(key); onRegisterKey(key);
mTimerProxy.startKeyRepeatTimer(this); mTimerProxy.startKeyRepeatTimer(this);
} }
@ -857,7 +917,7 @@ public class PointerTracker {
} }
private void startLongPressTimer(Key key) { private void startLongPressTimer(Key key) {
if (key != null && key.isLongPressEnabled() && !sGestureTracker.isInGesture()) { if (key != null && key.isLongPressEnabled() && !mInGesture) {
mTimerProxy.startLongPressTimer(this); mTimerProxy.startLongPressTimer(this);
} }
} }

View File

@ -1,147 +0,0 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.keyboard.internal;
import android.util.Log;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardActionListener;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.latin.InputPointers;
// TODO: Remove this class by consolidating with PointerTracker
public class GestureTracker {
private static final String TAG = GestureTracker.class.getSimpleName();
private static final boolean DEBUG_LISTENER = false;
// TODO: There should be an option to turn on/off the gesture input.
private static final boolean GESTURE_ON = true;
private static final GestureTracker sInstance = new GestureTracker();
private static final int MIN_RECOGNITION_TIME = 100;
private boolean mIsAlphabetKeyboard;
private boolean mIsPossibleGesture = false;
private boolean mInGesture = false;
private KeyboardActionListener mListener;
private int mLastRecognitionPointSize = 0;
private long mLastRecognitionTime = 0;
public static void init(KeyboardActionListener listner) {
sInstance.mListener = listner;
}
public static GestureTracker getInstance() {
return sInstance;
}
private GestureTracker() {
}
public void setKeyboard(Keyboard keyboard) {
mIsAlphabetKeyboard = keyboard.mId.isAlphabetKeyboard();
}
private void startBatchInput() {
if (DEBUG_LISTENER) {
Log.d(TAG, "onStartBatchInput");
}
mInGesture = true;
mListener.onStartBatchInput();
}
// TODO: The corresponding startBatchInput() is a private method. Reorganize the code.
public void endBatchInput() {
if (isInGesture()) {
final InputPointers batchPoints = PointerTracker.getAllBatchPoints();
if (DEBUG_LISTENER) {
Log.d(TAG, "onEndBatchInput: batchPoints=" + batchPoints.getPointerSize());
}
mListener.onEndBatchInput(batchPoints);
}
mInGesture = false;
clearBatchInputPoints();
}
public void abortBatchInput() {
mIsPossibleGesture = false;
mInGesture = false;
}
public boolean isInGesture() {
return mInGesture;
}
public void onDownEvent(PointerTracker tracker, int x, int y, long eventTime, Key key) {
mIsPossibleGesture = false;
// A gesture should start only from the letter key.
if (GESTURE_ON && mIsAlphabetKeyboard && key != null && Keyboard.isLetterCode(key.mCode)) {
mIsPossibleGesture = true;
tracker.getGestureStroke().addPoint(x, y, 0, false);
}
}
public void onMoveEvent(PointerTracker tracker, int x, int y, long eventTime,
boolean isHistorical, Key key) {
final int gestureTime = (int)(eventTime - tracker.getDownTime());
if (GESTURE_ON && mIsPossibleGesture) {
final GestureStroke stroke = tracker.getGestureStroke();
stroke.addPoint(x, y, gestureTime, isHistorical);
if (!isInGesture() && stroke.isStartOfAGesture(gestureTime)) {
startBatchInput();
}
}
if (key != null && isInGesture()) {
final InputPointers batchPoints = PointerTracker.getIncrementalBatchPoints();
if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
if (DEBUG_LISTENER) {
Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
}
mListener.onUpdateBatchInput(batchPoints);
}
}
}
public void onUpEvent(PointerTracker tracker, int x, int y, long eventTime) {
if (isInGesture()) {
final InputPointers batchPoints = PointerTracker.getAllBatchPoints();
if (DEBUG_LISTENER) {
Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
}
mListener.onUpdateBatchInput(batchPoints);
}
}
private void clearBatchInputPoints() {
PointerTracker.clearBatchInputPoints();
mLastRecognitionPointSize = 0;
mLastRecognitionTime = 0;
}
private boolean updateBatchInputRecognitionState(long eventTime, int size) {
if (size > mLastRecognitionPointSize
&& eventTime > mLastRecognitionTime + MIN_RECOGNITION_TIME) {
mLastRecognitionPointSize = size;
mLastRecognitionTime = eventTime;
return true;
}
return false;
}
}

View File

@ -60,6 +60,9 @@ public class InputPointers {
* @param length the number of pointers to be appended. * @param length the number of pointers to be appended.
*/ */
public void append(InputPointers src, int startPos, int length) { public void append(InputPointers src, int startPos, int length) {
if (length == 0) {
return;
}
mXCoordinates.append(src.mXCoordinates, startPos, length); mXCoordinates.append(src.mXCoordinates, startPos, length);
mYCoordinates.append(src.mYCoordinates, startPos, length); mYCoordinates.append(src.mYCoordinates, startPos, length);
mPointerIds.append(src.mPointerIds, startPos, length); mPointerIds.append(src.mPointerIds, startPos, length);