Skip to content
Snippets Groups Projects
Commit f39fccbd authored by Tadashi G. Takaoka's avatar Tadashi G. Takaoka
Browse files

Make GestureStroke as top level class

And make PointerTracker object has GestureStroke object.

Change-Id: Ibf5cfd593c4f13468368e01acb847589b0ab12e7
parent 3ec31f49
No related branches found
No related tags found
No related merge requests found
...@@ -22,8 +22,10 @@ import android.view.MotionEvent; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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) {
......
/*
* 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 @@ ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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);
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment