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.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.android.inputmethod.keyboard.internal.GestureStroke;
|
||||||
import com.android.inputmethod.keyboard.internal.GestureTracker;
|
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.LatinImeLogger;
|
import com.android.inputmethod.latin.LatinImeLogger;
|
||||||
import com.android.inputmethod.latin.ResearchLogger;
|
import com.android.inputmethod.latin.ResearchLogger;
|
||||||
import com.android.inputmethod.latin.define.ProductionFlag;
|
import com.android.inputmethod.latin.define.ProductionFlag;
|
||||||
|
@ -165,6 +167,8 @@ public class PointerTracker {
|
||||||
// Gesture tracker singleton instance
|
// Gesture tracker singleton instance
|
||||||
private static final GestureTracker sGestureTracker = GestureTracker.getInstance();
|
private static final GestureTracker sGestureTracker = GestureTracker.getInstance();
|
||||||
|
|
||||||
|
private final GestureStroke mGestureStroke;
|
||||||
|
|
||||||
public static void init(boolean hasDistinctMultitouch,
|
public static void init(boolean hasDistinctMultitouch,
|
||||||
boolean needsPhantomSuddenMoveEventHack) {
|
boolean needsPhantomSuddenMoveEventHack) {
|
||||||
if (hasDistinctMultitouch) {
|
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)
|
if (handler == null)
|
||||||
throw new NullPointerException();
|
throw new NullPointerException();
|
||||||
mPointerId = id;
|
mPointerId = id;
|
||||||
|
mGestureStroke = new GestureStroke(id);
|
||||||
setKeyDetectorInner(handler.getKeyDetector());
|
setKeyDetectorInner(handler.getKeyDetector());
|
||||||
mListener = handler.getKeyboardActionListener();
|
mListener = handler.getKeyboardActionListener();
|
||||||
mDrawingProxy = handler.getDrawingProxy();
|
mDrawingProxy = handler.getDrawingProxy();
|
||||||
|
@ -237,6 +274,10 @@ 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 (sGestureTracker.isInGesture()) {
|
||||||
|
@ -328,6 +369,8 @@ public class PointerTracker {
|
||||||
private void setKeyDetectorInner(KeyDetector keyDetector) {
|
private void setKeyDetectorInner(KeyDetector keyDetector) {
|
||||||
mKeyDetector = keyDetector;
|
mKeyDetector = keyDetector;
|
||||||
mKeyboard = keyDetector.getKeyboard();
|
mKeyboard = keyDetector.getKeyboard();
|
||||||
|
mGestureStroke.setGestureSampleLength(
|
||||||
|
mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
|
||||||
final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
|
final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
|
||||||
if (newKey != mCurrentKey) {
|
if (newKey != mCurrentKey) {
|
||||||
if (mDrawingProxy != null) {
|
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;
|
package com.android.inputmethod.keyboard.internal;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
|
||||||
|
|
||||||
import com.android.inputmethod.keyboard.Key;
|
import com.android.inputmethod.keyboard.Key;
|
||||||
import com.android.inputmethod.keyboard.Keyboard;
|
import com.android.inputmethod.keyboard.Keyboard;
|
||||||
|
@ -35,12 +34,6 @@ public class GestureTracker {
|
||||||
private static final GestureTracker sInstance = new GestureTracker();
|
private static final GestureTracker sInstance = new GestureTracker();
|
||||||
|
|
||||||
private static final int MIN_RECOGNITION_TIME = 100;
|
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 mIsAlphabetKeyboard;
|
||||||
private boolean mIsPossibleGesture = false;
|
private boolean mIsPossibleGesture = false;
|
||||||
|
@ -49,8 +42,6 @@ public class GestureTracker {
|
||||||
private KeyboardActionListener mListener;
|
private KeyboardActionListener mListener;
|
||||||
private SuggestedWords mSuggestions;
|
private SuggestedWords mSuggestions;
|
||||||
|
|
||||||
private final SparseArray<GestureStroke> mGestureStrokes = new SparseArray<GestureStroke>();
|
|
||||||
|
|
||||||
private int mLastRecognitionPointSize = 0;
|
private int mLastRecognitionPointSize = 0;
|
||||||
private long mLastRecognitionTime = 0;
|
private long mLastRecognitionTime = 0;
|
||||||
|
|
||||||
|
@ -67,8 +58,6 @@ public class GestureTracker {
|
||||||
|
|
||||||
public void setKeyboard(Keyboard keyboard) {
|
public void setKeyboard(Keyboard keyboard) {
|
||||||
mIsAlphabetKeyboard = keyboard.mId.isAlphabetKeyboard();
|
mIsAlphabetKeyboard = keyboard.mId.isAlphabetKeyboard();
|
||||||
GestureStroke.setGestureSampleLength(keyboard.mMostCommonKeyWidth / 2,
|
|
||||||
keyboard.mMostCommonKeyHeight / 6);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startBatchInput() {
|
private void startBatchInput() {
|
||||||
|
@ -107,7 +96,7 @@ public class GestureTracker {
|
||||||
// A gesture should start only from the letter key.
|
// A gesture should start only from the letter key.
|
||||||
if (GESTURE_ON && mIsAlphabetKeyboard && key != null && Keyboard.isLetterCode(key.mCode)) {
|
if (GESTURE_ON && mIsAlphabetKeyboard && key != null && Keyboard.isLetterCode(key.mCode)) {
|
||||||
mIsPossibleGesture = true;
|
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) {
|
boolean isHistorical, Key key) {
|
||||||
final int gestureTime = (int)(eventTime - tracker.getDownTime());
|
final int gestureTime = (int)(eventTime - tracker.getDownTime());
|
||||||
if (GESTURE_ON && mIsPossibleGesture) {
|
if (GESTURE_ON && mIsPossibleGesture) {
|
||||||
final GestureStroke stroke = addPointToStroke(x, y, gestureTime, tracker.mPointerId,
|
final GestureStroke stroke = tracker.getGestureStroke();
|
||||||
isHistorical);
|
stroke.addPoint(x, y, gestureTime, isHistorical);
|
||||||
if (!isInGesture() && stroke.isStartOfAGesture(gestureTime)) {
|
if (!isInGesture() && stroke.isStartOfAGesture(gestureTime)) {
|
||||||
startBatchInput();
|
startBatchInput();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key != null && isInGesture()) {
|
if (key != null && isInGesture()) {
|
||||||
final InputPointers batchPoints = getIncrementalBatchPoints();
|
final InputPointers batchPoints = PointerTracker.getIncrementalBatchPoints();
|
||||||
if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
|
if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
|
||||||
if (DEBUG_LISTENER) {
|
if (DEBUG_LISTENER) {
|
||||||
Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
|
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) {
|
public void onUpEvent(PointerTracker tracker, int x, int y, long eventTime) {
|
||||||
if (isInGesture()) {
|
if (isInGesture()) {
|
||||||
final InputPointers batchPoints = getAllBatchPoints();
|
final InputPointers batchPoints = PointerTracker.getAllBatchPoints();
|
||||||
if (DEBUG_LISTENER) {
|
if (DEBUG_LISTENER) {
|
||||||
Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
|
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() {
|
private void clearBatchInputPoints() {
|
||||||
final int strokeSize = mGestureStrokes.size();
|
PointerTracker.clearBatchInputPoints();
|
||||||
for (int index = 0; index < strokeSize; index++) {
|
|
||||||
final GestureStroke stroke = mGestureStrokes.valueAt(index);
|
|
||||||
stroke.reset();
|
|
||||||
}
|
|
||||||
mLastRecognitionPointSize = 0;
|
mLastRecognitionPointSize = 0;
|
||||||
mLastRecognitionTime = 0;
|
mLastRecognitionTime = 0;
|
||||||
}
|
}
|
||||||
|
@ -199,128 +147,4 @@ public class GestureTracker {
|
||||||
}
|
}
|
||||||
return false;
|
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