Make GestureStroke as top level class
And make PointerTracker object has GestureStroke object. Change-Id: Ibf5cfd593c4f13468368e01acb847589b0ab12e7
This commit is contained in:
parent
3ec31f4971
commit
f39fccbd0f
3 changed files with 199 additions and 183 deletions
|
@ -22,8 +22,10 @@ import android.view.MotionEvent;
|
|||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
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.latin.InputPointers;
|
||||
import com.android.inputmethod.latin.LatinImeLogger;
|
||||
import com.android.inputmethod.latin.ResearchLogger;
|
||||
import com.android.inputmethod.latin.define.ProductionFlag;
|
||||
|
@ -165,6 +167,8 @@ public class PointerTracker {
|
|||
// Gesture tracker singleton instance
|
||||
private static final GestureTracker sGestureTracker = GestureTracker.getInstance();
|
||||
|
||||
private final GestureStroke mGestureStroke;
|
||||
|
||||
public static void init(boolean hasDistinctMultitouch,
|
||||
boolean needsPhantomSuddenMoveEventHack) {
|
||||
if (hasDistinctMultitouch) {
|
||||
|
@ -222,10 +226,43 @@ public class PointerTracker {
|
|||
}
|
||||
}
|
||||
|
||||
public PointerTracker(int id, KeyEventHandler handler) {
|
||||
// The working and returning object of the following methods,
|
||||
// {@link #getIncrementalBatchPoints()} and {@link #getAllBatchPoints()}.
|
||||
private static final InputPointers mAggregatedPointers = new InputPointers();
|
||||
|
||||
// TODO: This method is called only from GestureTracker and should address the thread-safty
|
||||
// issue soon.
|
||||
public static InputPointers getIncrementalBatchPoints() {
|
||||
final InputPointers pointers = mAggregatedPointers;
|
||||
pointers.reset();
|
||||
for (final PointerTracker tracker : sTrackers) {
|
||||
tracker.getGestureStroke().appendIncrementalBatchPoints(pointers);
|
||||
}
|
||||
return pointers;
|
||||
}
|
||||
|
||||
// TODO: This method is called only from GestureTracker and should address the thread-safety
|
||||
// issue soon.
|
||||
public static InputPointers getAllBatchPoints() {
|
||||
final InputPointers pointers = mAggregatedPointers;
|
||||
pointers.reset();
|
||||
for (final PointerTracker tracker : sTrackers) {
|
||||
tracker.getGestureStroke().appendAllBatchPoints(pointers);
|
||||
}
|
||||
return pointers;
|
||||
}
|
||||
|
||||
public static void clearBatchInputPoints() {
|
||||
for (final PointerTracker tracker : sTrackers) {
|
||||
tracker.getGestureStroke().reset();
|
||||
}
|
||||
}
|
||||
|
||||
private PointerTracker(int id, KeyEventHandler handler) {
|
||||
if (handler == null)
|
||||
throw new NullPointerException();
|
||||
mPointerId = id;
|
||||
mGestureStroke = new GestureStroke(id);
|
||||
setKeyDetectorInner(handler.getKeyDetector());
|
||||
mListener = handler.getKeyboardActionListener();
|
||||
mDrawingProxy = handler.getDrawingProxy();
|
||||
|
@ -237,6 +274,10 @@ public class PointerTracker {
|
|||
return mKeyPreviewText;
|
||||
}
|
||||
|
||||
public GestureStroke getGestureStroke() {
|
||||
return mGestureStroke;
|
||||
}
|
||||
|
||||
// Returns true if keyboard has been changed by this callback.
|
||||
private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) {
|
||||
if (sGestureTracker.isInGesture()) {
|
||||
|
@ -328,6 +369,8 @@ public class PointerTracker {
|
|||
private void setKeyDetectorInner(KeyDetector keyDetector) {
|
||||
mKeyDetector = keyDetector;
|
||||
mKeyboard = keyDetector.getKeyboard();
|
||||
mGestureStroke.setGestureSampleLength(
|
||||
mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
|
||||
final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
|
||||
if (newKey != mCurrentKey) {
|
||||
if (mDrawingProxy != null) {
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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.FloatMath;
|
||||
|
||||
import com.android.inputmethod.latin.InputPointers;
|
||||
|
||||
public class GestureStroke {
|
||||
private final int mPointerId;
|
||||
private final InputPointers mInputPointers = new InputPointers();
|
||||
private float mLength;
|
||||
private float mAngle;
|
||||
private int mIncrementalRecognitionPoint;
|
||||
private boolean mHasSharpCorner;
|
||||
private long mLastPointTime;
|
||||
private int mLastPointX;
|
||||
private int mLastPointY;
|
||||
|
||||
private int mMinGestureLength;
|
||||
private int mMinGestureSampleLength;
|
||||
|
||||
// TODO: Tune these parameters.
|
||||
private static final float MIN_GESTURE_DETECTION_RATIO_TO_KEY_WIDTH = 1.0f / 4.0f;
|
||||
private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT = 1.0f / 6.0f;
|
||||
private static final int MIN_GESTURE_DURATION = 100; // msec
|
||||
private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f; // dip/msec
|
||||
private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float)(Math.PI / 4.0f);
|
||||
|
||||
private static final float DOUBLE_PI = (float)(2 * Math.PI);
|
||||
|
||||
public GestureStroke(int pointerId) {
|
||||
mPointerId = pointerId;
|
||||
reset();
|
||||
}
|
||||
|
||||
public void setGestureSampleLength(final int keyWidth, final int keyHeight) {
|
||||
mMinGestureLength = (int)(keyWidth * MIN_GESTURE_DETECTION_RATIO_TO_KEY_WIDTH);
|
||||
mMinGestureSampleLength = (int)(keyHeight * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT);
|
||||
}
|
||||
|
||||
public boolean isStartOfAGesture(int downDuration) {
|
||||
return downDuration > MIN_GESTURE_DURATION && mLength > mMinGestureLength;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mLength = 0;
|
||||
mAngle = 0;
|
||||
mIncrementalRecognitionPoint = 0;
|
||||
mHasSharpCorner = false;
|
||||
mLastPointTime = 0;
|
||||
mInputPointers.reset();
|
||||
}
|
||||
|
||||
private void updateLastPoint(final int x, final int y, final int time) {
|
||||
mLastPointTime = time;
|
||||
mLastPointX = x;
|
||||
mLastPointY = y;
|
||||
}
|
||||
|
||||
public void addPoint(final int x, final int y, final int time, final boolean isHistorical) {
|
||||
final int size = mInputPointers.getPointerSize();
|
||||
if (size == 0) {
|
||||
mInputPointers.addPointer(x, y, mPointerId, time);
|
||||
if (!isHistorical) {
|
||||
updateLastPoint(x, y, time);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final int[] xCoords = mInputPointers.getXCoordinates();
|
||||
final int[] yCoords = mInputPointers.getYCoordinates();
|
||||
final int lastX = xCoords[size - 1];
|
||||
final int lastY = yCoords[size - 1];
|
||||
final float dist = getDistance(lastX, lastY, x, y);
|
||||
if (dist > mMinGestureSampleLength) {
|
||||
mInputPointers.addPointer(x, y, mPointerId, time);
|
||||
mLength += dist;
|
||||
final float angle = getAngle(lastX, lastY, x, y);
|
||||
if (size > 1) {
|
||||
float curvature = getAngleDiff(angle, mAngle);
|
||||
if (curvature > GESTURE_RECOG_CURVATURE_THRESHOLD) {
|
||||
if (size > mIncrementalRecognitionPoint) {
|
||||
mIncrementalRecognitionPoint = size;
|
||||
}
|
||||
mHasSharpCorner = true;
|
||||
}
|
||||
if (!mHasSharpCorner) {
|
||||
mIncrementalRecognitionPoint = size;
|
||||
}
|
||||
}
|
||||
mAngle = angle;
|
||||
}
|
||||
|
||||
if (!isHistorical) {
|
||||
final int duration = (int)(time - mLastPointTime);
|
||||
if (mLastPointTime != 0 && duration > 0) {
|
||||
final float speed = getDistance(mLastPointX, mLastPointY, x, y) / duration;
|
||||
if (speed < GESTURE_RECOG_SPEED_THRESHOLD) {
|
||||
mIncrementalRecognitionPoint = size;
|
||||
}
|
||||
}
|
||||
updateLastPoint(x, y, time);
|
||||
}
|
||||
}
|
||||
|
||||
public void appendAllBatchPoints(final InputPointers out) {
|
||||
out.append(mInputPointers, 0, mInputPointers.getPointerSize());
|
||||
}
|
||||
|
||||
public void appendIncrementalBatchPoints(final InputPointers out) {
|
||||
out.append(mInputPointers, 0, mIncrementalRecognitionPoint);
|
||||
}
|
||||
|
||||
private static float getDistance(final int p1x, final int p1y,
|
||||
final int p2x, final int p2y) {
|
||||
final float dx = p1x - p2x;
|
||||
final float dy = p1y - p2y;
|
||||
// TODO: Optimize out this {@link FloatMath#sqrt(float)} call.
|
||||
return FloatMath.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
private static float getAngle(final int p1x, final int p1y, final int p2x, final int p2y) {
|
||||
final int dx = p1x - p2x;
|
||||
final int dy = p1y - p2y;
|
||||
if (dx == 0 && dy == 0) return 0;
|
||||
return (float)Math.atan2(dy, dx);
|
||||
}
|
||||
|
||||
private static float getAngleDiff(final float a1, final float a2) {
|
||||
final float diff = Math.abs(a1 - a2);
|
||||
if (diff > Math.PI) {
|
||||
return DOUBLE_PI - diff;
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
}
|
|
@ -15,7 +15,6 @@
|
|||
package com.android.inputmethod.keyboard.internal;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.inputmethod.keyboard.Key;
|
||||
import com.android.inputmethod.keyboard.Keyboard;
|
||||
|
@ -35,12 +34,6 @@ public class GestureTracker {
|
|||
private static final GestureTracker sInstance = new GestureTracker();
|
||||
|
||||
private static final int MIN_RECOGNITION_TIME = 100;
|
||||
private static final int MIN_GESTURE_DURATION = 200;
|
||||
|
||||
private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f;
|
||||
private static final float SQUARED_GESTURE_RECOG_SPEED_THRESHOLD =
|
||||
GESTURE_RECOG_SPEED_THRESHOLD * GESTURE_RECOG_SPEED_THRESHOLD;
|
||||
private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float) (Math.PI / 4);
|
||||
|
||||
private boolean mIsAlphabetKeyboard;
|
||||
private boolean mIsPossibleGesture = false;
|
||||
|
@ -49,8 +42,6 @@ public class GestureTracker {
|
|||
private KeyboardActionListener mListener;
|
||||
private SuggestedWords mSuggestions;
|
||||
|
||||
private final SparseArray<GestureStroke> mGestureStrokes = new SparseArray<GestureStroke>();
|
||||
|
||||
private int mLastRecognitionPointSize = 0;
|
||||
private long mLastRecognitionTime = 0;
|
||||
|
||||
|
@ -67,8 +58,6 @@ public class GestureTracker {
|
|||
|
||||
public void setKeyboard(Keyboard keyboard) {
|
||||
mIsAlphabetKeyboard = keyboard.mId.isAlphabetKeyboard();
|
||||
GestureStroke.setGestureSampleLength(keyboard.mMostCommonKeyWidth / 2,
|
||||
keyboard.mMostCommonKeyHeight / 6);
|
||||
}
|
||||
|
||||
private void startBatchInput() {
|
||||
|
@ -107,7 +96,7 @@ public class GestureTracker {
|
|||
// A gesture should start only from the letter key.
|
||||
if (GESTURE_ON && mIsAlphabetKeyboard && key != null && Keyboard.isLetterCode(key.mCode)) {
|
||||
mIsPossibleGesture = true;
|
||||
addPointToStroke(x, y, 0, tracker.mPointerId, false);
|
||||
tracker.getGestureStroke().addPoint(x, y, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,15 +104,15 @@ public class GestureTracker {
|
|||
boolean isHistorical, Key key) {
|
||||
final int gestureTime = (int)(eventTime - tracker.getDownTime());
|
||||
if (GESTURE_ON && mIsPossibleGesture) {
|
||||
final GestureStroke stroke = addPointToStroke(x, y, gestureTime, tracker.mPointerId,
|
||||
isHistorical);
|
||||
final GestureStroke stroke = tracker.getGestureStroke();
|
||||
stroke.addPoint(x, y, gestureTime, isHistorical);
|
||||
if (!isInGesture() && stroke.isStartOfAGesture(gestureTime)) {
|
||||
startBatchInput();
|
||||
}
|
||||
}
|
||||
|
||||
if (key != null && isInGesture()) {
|
||||
final InputPointers batchPoints = getIncrementalBatchPoints();
|
||||
final InputPointers batchPoints = PointerTracker.getIncrementalBatchPoints();
|
||||
if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
|
||||
if (DEBUG_LISTENER) {
|
||||
Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
|
||||
|
@ -135,7 +124,7 @@ public class GestureTracker {
|
|||
|
||||
public void onUpEvent(PointerTracker tracker, int x, int y, long eventTime) {
|
||||
if (isInGesture()) {
|
||||
final InputPointers batchPoints = getAllBatchPoints();
|
||||
final InputPointers batchPoints = PointerTracker.getAllBatchPoints();
|
||||
if (DEBUG_LISTENER) {
|
||||
Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
|
||||
}
|
||||
|
@ -143,49 +132,8 @@ public class GestureTracker {
|
|||
}
|
||||
}
|
||||
|
||||
private GestureStroke addPointToStroke(int x, int y, int time, int pointerId,
|
||||
boolean isHistorical) {
|
||||
GestureStroke stroke = mGestureStrokes.get(pointerId);
|
||||
if (stroke == null) {
|
||||
stroke = new GestureStroke(pointerId);
|
||||
mGestureStrokes.put(pointerId, stroke);
|
||||
}
|
||||
stroke.addPoint(x, y, time, isHistorical);
|
||||
return stroke;
|
||||
}
|
||||
|
||||
// The working and return object of the following methods, {@link #getIncrementalBatchPoints()}
|
||||
// and {@link #getAllBatchPoints()}.
|
||||
private final InputPointers mAggregatedPointers = new InputPointers();
|
||||
|
||||
private InputPointers getIncrementalBatchPoints() {
|
||||
final InputPointers pointers = mAggregatedPointers;
|
||||
pointers.reset();
|
||||
final int strokeSize = mGestureStrokes.size();
|
||||
for (int index = 0; index < strokeSize; index++) {
|
||||
final GestureStroke stroke = mGestureStrokes.valueAt(index);
|
||||
stroke.appendIncrementalBatchPoints(pointers);
|
||||
}
|
||||
return pointers;
|
||||
}
|
||||
|
||||
private InputPointers getAllBatchPoints() {
|
||||
final InputPointers pointers = mAggregatedPointers;
|
||||
pointers.reset();
|
||||
final int strokeSize = mGestureStrokes.size();
|
||||
for (int index = 0; index < strokeSize; index++) {
|
||||
final GestureStroke stroke = mGestureStrokes.valueAt(index);
|
||||
stroke.appendAllBatchPoints(pointers);
|
||||
}
|
||||
return pointers;
|
||||
}
|
||||
|
||||
private void clearBatchInputPoints() {
|
||||
final int strokeSize = mGestureStrokes.size();
|
||||
for (int index = 0; index < strokeSize; index++) {
|
||||
final GestureStroke stroke = mGestureStrokes.valueAt(index);
|
||||
stroke.reset();
|
||||
}
|
||||
PointerTracker.clearBatchInputPoints();
|
||||
mLastRecognitionPointSize = 0;
|
||||
mLastRecognitionTime = 0;
|
||||
}
|
||||
|
@ -199,128 +147,4 @@ public class GestureTracker {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class GestureStroke {
|
||||
private final int mPointerId;
|
||||
private final InputPointers mInputPointers = new InputPointers();
|
||||
private float mLength;
|
||||
private float mAngle;
|
||||
private int mIncrementalRecognitionPoint;
|
||||
private boolean mHasSharpCorner;
|
||||
private long mLastPointTime;
|
||||
private int mLastPointX;
|
||||
private int mLastPointY;
|
||||
|
||||
private static int sMinGestureLength;
|
||||
private static int sSquaredGestureSampleLength;
|
||||
|
||||
private static final float DOUBLE_PI = (float)(2 * Math.PI);
|
||||
|
||||
public static void setGestureSampleLength(final int minGestureLength,
|
||||
final int sampleLength) {
|
||||
sMinGestureLength = minGestureLength;
|
||||
sSquaredGestureSampleLength = sampleLength * sampleLength;
|
||||
}
|
||||
|
||||
public GestureStroke(int pointerId) {
|
||||
mPointerId = pointerId;
|
||||
reset();
|
||||
}
|
||||
|
||||
public boolean isStartOfAGesture(int downDuration) {
|
||||
return downDuration > MIN_GESTURE_DURATION / 2 && mLength > sMinGestureLength / 2;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mLength = 0;
|
||||
mAngle = 0;
|
||||
mIncrementalRecognitionPoint = 0;
|
||||
mHasSharpCorner = false;
|
||||
mLastPointTime = 0;
|
||||
mInputPointers.reset();
|
||||
}
|
||||
|
||||
private void updateLastPoint(final int x, final int y, final int time) {
|
||||
mLastPointTime = time;
|
||||
mLastPointX = x;
|
||||
mLastPointY = y;
|
||||
}
|
||||
|
||||
public void addPoint(final int x, final int y, final int time, final boolean isHistorical) {
|
||||
final int size = mInputPointers.getPointerSize();
|
||||
if (size == 0) {
|
||||
mInputPointers.addPointer(x, y, mPointerId, time);
|
||||
if (!isHistorical) {
|
||||
updateLastPoint(x, y, time);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final int[] xCoords = mInputPointers.getXCoordinates();
|
||||
final int[] yCoords = mInputPointers.getYCoordinates();
|
||||
final int lastX = xCoords[size - 1];
|
||||
final int lastY = yCoords[size - 1];
|
||||
final float dist = squaredDistance(lastX, lastY, x, y);
|
||||
if (dist > sSquaredGestureSampleLength) {
|
||||
mInputPointers.addPointer(x, y, mPointerId, time);
|
||||
mLength += dist;
|
||||
final float angle = angle(lastX, lastY, x, y);
|
||||
if (size > 1) {
|
||||
float curvature = getAngleDiff(angle, mAngle);
|
||||
if (curvature > GESTURE_RECOG_CURVATURE_THRESHOLD) {
|
||||
if (size > mIncrementalRecognitionPoint) {
|
||||
mIncrementalRecognitionPoint = size;
|
||||
}
|
||||
mHasSharpCorner = true;
|
||||
}
|
||||
if (!mHasSharpCorner) {
|
||||
mIncrementalRecognitionPoint = size;
|
||||
}
|
||||
}
|
||||
mAngle = angle;
|
||||
}
|
||||
|
||||
if (!isHistorical) {
|
||||
final int duration = (int)(time - mLastPointTime);
|
||||
if (mLastPointTime != 0 && duration > 0) {
|
||||
final int squaredDuration = duration * duration;
|
||||
final float squaredSpeed =
|
||||
squaredDistance(mLastPointX, mLastPointY, x, y) / squaredDuration;
|
||||
if (squaredSpeed < SQUARED_GESTURE_RECOG_SPEED_THRESHOLD) {
|
||||
mIncrementalRecognitionPoint = size;
|
||||
}
|
||||
}
|
||||
updateLastPoint(x, y, time);
|
||||
}
|
||||
}
|
||||
|
||||
private float getAngleDiff(float a1, float a2) {
|
||||
final float diff = Math.abs(a1 - a2);
|
||||
if (diff > Math.PI) {
|
||||
return DOUBLE_PI - diff;
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
public void appendAllBatchPoints(InputPointers out) {
|
||||
out.append(mInputPointers, 0, mInputPointers.getPointerSize());
|
||||
}
|
||||
|
||||
public void appendIncrementalBatchPoints(InputPointers out) {
|
||||
out.append(mInputPointers, 0, mIncrementalRecognitionPoint);
|
||||
}
|
||||
}
|
||||
|
||||
static float squaredDistance(int p1x, int p1y, int p2x, int p2y) {
|
||||
final float dx = p1x - p2x;
|
||||
final float dy = p1y - p2y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
static float angle(int p1x, int p1y, int p2x, int p2y) {
|
||||
final int dx = p1x - p2x;
|
||||
final int dy = p1y - p2y;
|
||||
if (dx == 0 && dy == 0) return 0;
|
||||
return (float)Math.atan2(dy, dx);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue