Cancel gesture typing by sliding out from keyboard

Bug: 7594165
Change-Id: I7849d763e49b57716e8418fb8b6f90eca3a5d2ec
main
Tadashi G. Takaoka 2012-11-22 15:39:28 +09:00
parent befc1a05c9
commit b3f789799a
8 changed files with 93 additions and 31 deletions

View File

@ -80,6 +80,8 @@ public interface KeyboardActionListener {
*/ */
public void onEndBatchInput(InputPointers batchPointers); public void onEndBatchInput(InputPointers batchPointers);
public void onCancelBatchInput();
/** /**
* Called when user released a finger outside any key. * Called when user released a finger outside any key.
*/ */
@ -107,6 +109,8 @@ public interface KeyboardActionListener {
@Override @Override
public void onEndBatchInput(InputPointers batchPointers) {} public void onEndBatchInput(InputPointers batchPointers) {}
@Override @Override
public void onCancelBatchInput() {}
@Override
public void onCancelInput() {} public void onCancelInput() {}
@Override @Override
public boolean onCustomRequest(int requestCode) { public boolean onCustomRequest(int requestCode) {

View File

@ -305,8 +305,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
// true if keyboard layout has been changed. // true if keyboard layout has been changed.
private boolean mKeyboardLayoutHasBeenChanged; private boolean mKeyboardLayoutHasBeenChanged;
// true if event is already translated to a key action. // true if this pointer is no longer tracking touch event.
private boolean mKeyAlreadyProcessed; private boolean mIsTrackingCanceled;
// true if this pointer has been long-pressed and is showing a more keys panel. // true if this pointer has been long-pressed and is showing a more keys panel.
private boolean mIsShowingMoreKeysPanel; private boolean mIsShowingMoreKeysPanel;
@ -517,7 +517,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
mKeyboard = keyDetector.getKeyboard(); mKeyboard = keyDetector.getKeyboard();
final int keyWidth = mKeyboard.mMostCommonKeyWidth; final int keyWidth = mKeyboard.mMostCommonKeyWidth;
final int keyHeight = mKeyboard.mMostCommonKeyHeight; final int keyHeight = mKeyboard.mMostCommonKeyHeight;
mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth); mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth, mKeyboard.mOccupiedHeight);
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) {
@ -730,13 +730,15 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
synchronized (sAggregratedPointers) { synchronized (sAggregratedPointers) {
mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers); mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
if (getActivePointerTrackerCount() == 1) { if (getActivePointerTrackerCount() == 1) {
if (DEBUG_LISTENER) {
Log.d(TAG, String.format("[%d] onEndBatchInput : batchPoints=%d",
mPointerId, sAggregratedPointers.getPointerSize()));
}
sInGesture = false; sInGesture = false;
sTimeRecorder.onEndBatchInput(eventTime); sTimeRecorder.onEndBatchInput(eventTime);
mListener.onEndBatchInput(sAggregratedPointers); if (!mIsTrackingCanceled) {
if (DEBUG_LISTENER) {
Log.d(TAG, String.format("[%d] onEndBatchInput : batchPoints=%d",
mPointerId, sAggregratedPointers.getPointerSize()));
}
mListener.onEndBatchInput(sAggregratedPointers);
}
} }
} }
mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this)); mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this));
@ -784,7 +786,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance); ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance);
} }
mKeyAlreadyProcessed = true; cancelTracking();
return; return;
} }
} }
@ -821,7 +823,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|| (key != null && key.isModifier()) || (key != null && key.isModifier())
|| mKeyDetector.alwaysAllowsSlidingInput(); || mKeyDetector.alwaysAllowsSlidingInput();
mKeyboardLayoutHasBeenChanged = false; mKeyboardLayoutHasBeenChanged = false;
mKeyAlreadyProcessed = false; mIsTrackingCanceled = false;
resetSlidingKeyInput(); resetSlidingKeyInput();
if (key != null) { if (key != null) {
// This onPress call may have changed keyboard layout. Those cases are detected at // This onPress call may have changed keyboard layout. Those cases are detected at
@ -853,7 +855,17 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
final boolean isMajorEvent, final Key key) { final boolean isMajorEvent, final Key key) {
final int gestureTime = (int)(eventTime - sGestureFirstDownTime); final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
if (mIsDetectingGesture) { if (mIsDetectingGesture) {
mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isMajorEvent); final boolean onValidArea = mGestureStrokeWithPreviewPoints.addPointOnKeyboard(
x, y, gestureTime, isMajorEvent);
if (!onValidArea) {
sPointerTrackerQueue.cancelAllPointerTracker();
if (DEBUG_LISTENER) {
Log.d(TAG, String.format("[%d] onCancelBatchInput: batchPoints=%d",
mPointerId, sAggregratedPointers.getPointerSize()));
}
mListener.onCancelBatchInput();
return;
}
mayStartBatchInput(key); mayStartBatchInput(key);
if (sInGesture) { if (sInGesture) {
mayUpdateBatchInput(eventTime, key); mayUpdateBatchInput(eventTime, key);
@ -865,7 +877,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (DEBUG_MOVE_EVENT) { if (DEBUG_MOVE_EVENT) {
printTouchEvent("onMoveEvent:", x, y, eventTime); printTouchEvent("onMoveEvent:", x, y, eventTime);
} }
if (mKeyAlreadyProcessed) { if (mIsTrackingCanceled) {
return; return;
} }
@ -979,11 +991,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
+ " detected sliding finger while multi touching", mPointerId)); + " detected sliding finger while multi touching", mPointerId));
} }
onUpEvent(x, y, eventTime); onUpEvent(x, y, eventTime);
mKeyAlreadyProcessed = true; cancelTracking();
setReleasedKeyGraphics(oldKey); setReleasedKeyGraphics(oldKey);
} else { } else {
if (!mIsDetectingGesture) { if (!mIsDetectingGesture) {
mKeyAlreadyProcessed = true; cancelTracking();
} }
setReleasedKeyGraphics(oldKey); setReleasedKeyGraphics(oldKey);
} }
@ -997,7 +1009,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
onMoveToNewKey(null, x, y); onMoveToNewKey(null, x, y);
} else { } else {
if (!mIsDetectingGesture) { if (!mIsDetectingGesture) {
mKeyAlreadyProcessed = true; cancelTracking();
} }
} }
} }
@ -1060,7 +1072,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime); printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime);
} }
onUpEventInternal(eventTime); onUpEventInternal(eventTime);
mKeyAlreadyProcessed = true; cancelTracking();
} }
private void onUpEventInternal(final long eventTime) { private void onUpEventInternal(final long eventTime) {
@ -1084,7 +1096,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return; return;
} }
if (mKeyAlreadyProcessed) { if (mIsTrackingCanceled) {
return; return;
} }
if (currentKey != null && !currentKey.isRepeatable()) { if (currentKey != null && !currentKey.isRepeatable()) {
@ -1098,8 +1110,13 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
onDownEvent(x, y, SystemClock.uptimeMillis(), handler); onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
} }
@Override
public void cancelTracking() {
mIsTrackingCanceled = true;
}
public void onLongPressed() { public void onLongPressed() {
mKeyAlreadyProcessed = true; cancelTracking();
setReleasedKeyGraphics(mCurrentKey); setReleasedKeyGraphics(mCurrentKey);
sPointerTrackerQueue.remove(this); sPointerTrackerQueue.remove(this);
} }
@ -1202,6 +1219,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
final Key key = mKeyDetector.detectHitKey(x, y); final Key key = mKeyDetector.detectHitKey(x, y);
final String code = KeyDetector.printableCode(key); final String code = KeyDetector.printableCode(key);
Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId, 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));
} }
} }

View File

@ -27,6 +27,10 @@ public class GestureStroke {
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
private static final boolean DEBUG_SPEED = false; private static final boolean DEBUG_SPEED = false;
// The height of extra area above the keyboard to draw gesture trails.
// Proportional to the keyboard height.
public static final float EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO = 0.25f;
public static final int DEFAULT_CAPACITY = 128; public static final int DEFAULT_CAPACITY = 128;
private final int mPointerId; private final int mPointerId;
@ -37,6 +41,8 @@ public class GestureStroke {
private final GestureStrokeParams mParams; private final GestureStrokeParams mParams;
private int mKeyWidth; // pixel private int mKeyWidth; // pixel
private int mMinYCoordinate; // pixel
private int mMaxYCoordinate; // pixel
// Static threshold for starting gesture detection // Static threshold for starting gesture detection
private int mDetectFastMoveSpeedThreshold; // pixel /sec private int mDetectFastMoveSpeedThreshold; // pixel /sec
private int mDetectFastMoveTime; private int mDetectFastMoveTime;
@ -135,8 +141,10 @@ public class GestureStroke {
mParams = params; mParams = params;
} }
public void setKeyboardGeometry(final int keyWidth) { public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
mKeyWidth = keyWidth; mKeyWidth = keyWidth;
mMinYCoordinate = -(int)(keyboardHeight * EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
mMaxYCoordinate = keyboardHeight - 1;
// TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key? // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
mDetectFastMoveSpeedThreshold = (int)(keyWidth * mParams.mDetectFastMoveSpeedThreshold); mDetectFastMoveSpeedThreshold = (int)(keyWidth * mParams.mDetectFastMoveSpeedThreshold);
mGestureDynamicDistanceThresholdFrom = mGestureDynamicDistanceThresholdFrom =
@ -167,7 +175,7 @@ public class GestureStroke {
elapsedTimeAfterTyping, mAfterFastTyping ? " afterFastTyping" : "")); elapsedTimeAfterTyping, mAfterFastTyping ? " afterFastTyping" : ""));
} }
final int elapsedTimeFromFirstDown = (int)(downTime - gestureFirstDownTime); final int elapsedTimeFromFirstDown = (int)(downTime - gestureFirstDownTime);
addPoint(x, y, elapsedTimeFromFirstDown, true /* isMajorEvent */); addPointOnKeyboard(x, y, elapsedTimeFromFirstDown, true /* isMajorEvent */);
} }
private int getGestureDynamicDistanceThreshold(final int deltaTime) { private int getGestureDynamicDistanceThreshold(final int deltaTime) {
@ -277,7 +285,17 @@ public class GestureStroke {
return dist; return dist;
} }
public void addPoint(final int x, final int y, final int time, final boolean isMajorEvent) { /**
* Add a touch event as a gesture point. Returns true if the touch event is on the valid
* gesture area.
* @param x the x-coordinate of the touch event
* @param y the y-coordinate of the touch event
* @param time the elapsed time in millisecond from the first gesture down
* @param isMajorEvent false if this is a historical move event
* @return true if the touch event is on the valid gesture area
*/
public boolean addPointOnKeyboard(final int x, final int y, final int time,
final boolean isMajorEvent) {
final int size = mEventTimes.getLength(); final int size = mEventTimes.getLength();
if (size <= 0) { if (size <= 0) {
// Down event // Down event
@ -293,6 +311,7 @@ public class GestureStroke {
updateIncrementalRecognitionSize(x, y, time); updateIncrementalRecognitionSize(x, y, time);
updateMajorEvent(x, y, time); updateMajorEvent(x, y, time);
} }
return y >= mMinYCoordinate && y < mMaxYCoordinate;
} }
private void updateIncrementalRecognitionSize(final int x, final int y, final int time) { private void updateIncrementalRecognitionSize(final int x, final int y, final int time) {

View File

@ -56,8 +56,8 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke {
} }
@Override @Override
public void setKeyboardGeometry(final int keyWidth) { public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
super.setKeyboardGeometry(keyWidth); super.setKeyboardGeometry(keyWidth, keyboardHeight);
final float sampleLength = keyWidth * MIN_PREVIEW_SAMPLE_LENGTH_RATIO_TO_KEY_WIDTH; final float sampleLength = keyWidth * MIN_PREVIEW_SAMPLE_LENGTH_RATIO_TO_KEY_WIDTH;
mMinPreviewSampleLengthSquare = (int)(sampleLength * sampleLength); mMinPreviewSampleLengthSquare = (int)(sampleLength * sampleLength);
} }
@ -69,8 +69,9 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke {
} }
@Override @Override
public void addPoint(final int x, final int y, final int time, final boolean isMajorEvent) { public boolean addPointOnKeyboard(final int x, final int y, final int time,
super.addPoint(x, y, time, isMajorEvent); final boolean isMajorEvent) {
final boolean onValidArea = super.addPointOnKeyboard(x, y, time, isMajorEvent);
if (isMajorEvent || needsSampling(x, y)) { if (isMajorEvent || needsSampling(x, y)) {
mPreviewEventTimes.add(time); mPreviewEventTimes.add(time);
mPreviewXCoordinates.add(x); mPreviewXCoordinates.add(x);
@ -78,6 +79,7 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke {
mLastX = x; mLastX = x;
mLastY = y; mLastY = y;
} }
return onValidArea;
} }
public void appendPreviewStroke(final ResizableIntArray eventTimes, public void appendPreviewStroke(final ResizableIntArray eventTimes,

View File

@ -30,6 +30,7 @@ public final class PointerTrackerQueue {
public boolean isModifier(); public boolean isModifier();
public boolean isInSlidingKeyInput(); public boolean isInSlidingKeyInput();
public void onPhantomUpEvent(long eventTime); public void onPhantomUpEvent(long eventTime);
public void cancelTracking();
} }
private static final int INITIAL_CAPACITY = 10; private static final int INITIAL_CAPACITY = 10;
@ -182,6 +183,15 @@ public final class PointerTrackerQueue {
return false; return false;
} }
public synchronized void cancelAllPointerTracker() {
final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
final int arraySize = mArraySize;
for (int index = 0; index < arraySize; index++) {
final Element element = expandableArray.get(index);
element.cancelTracking();
}
}
@Override @Override
public synchronized String toString() { public synchronized String toString() {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();

View File

@ -40,10 +40,6 @@ import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
public final class PreviewPlacerView extends RelativeLayout { public final class PreviewPlacerView extends RelativeLayout {
// The height of extra area above the keyboard to draw gesture trails.
// Proportional to the keyboard height.
private static final float EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO = 0.25f;
private final int mGestureFloatingPreviewTextColor; private final int mGestureFloatingPreviewTextColor;
private final int mGestureFloatingPreviewTextOffset; private final int mGestureFloatingPreviewTextOffset;
private final int mGestureFloatingPreviewColor; private final int mGestureFloatingPreviewColor;
@ -175,7 +171,7 @@ public final class PreviewPlacerView extends RelativeLayout {
public void setKeyboardViewGeometry(final int x, final int y, final int w, final int h) { public void setKeyboardViewGeometry(final int x, final int y, final int w, final int h) {
mKeyboardViewOriginX = x; mKeyboardViewOriginX = x;
mKeyboardViewOriginY = y; mKeyboardViewOriginY = y;
mOffscreenOffsetY = (int)(h * EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO); mOffscreenOffsetY = (int)(h * GestureStroke.EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
mOffscreenWidth = w; mOffscreenWidth = w;
mOffscreenHeight = mOffscreenOffsetY + h; mOffscreenHeight = mOffscreenOffsetY + h;
} }

View File

@ -1528,6 +1528,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
.sendToTarget(); .sendToTarget();
} }
public void onCancelBatchInput(final LatinIME latinIme) {
mInBatchInput = false;
latinIme.mHandler.showGesturePreviewAndSuggestionStrip(
SuggestedWords.EMPTY, true /* dismissGestureFloatingPreviewText */);
}
// Run in the UI thread. // Run in the UI thread.
public synchronized SuggestedWords onEndBatchInput(final InputPointers batchPointers, public synchronized SuggestedWords onEndBatchInput(final InputPointers batchPointers,
final LatinIME latinIme) { final LatinIME latinIme) {
@ -1613,6 +1619,11 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
mKeyboardSwitcher.onCancelInput(); mKeyboardSwitcher.onCancelInput();
} }
@Override
public void onCancelBatchInput() {
BatchInputUpdater.getInstance().onCancelBatchInput(this);
}
private void handleBackspace(final int spaceState) { private void handleBackspace(final int spaceState) {
// In many cases, we may have to put the keyboard in auto-shift state again. However // In many cases, we may have to put the keyboard in auto-shift state again. However
// we want to wait a few milliseconds before doing it to avoid the keyboard flashing // we want to wait a few milliseconds before doing it to avoid the keyboard flashing

View File

@ -48,6 +48,9 @@ public class PointerTrackerQueueTests extends AndroidTestCase {
mPhantomUpEventTime = eventTime + sPhantomUpCount; mPhantomUpEventTime = eventTime + sPhantomUpCount;
} }
@Override
public void cancelTracking() {}
@Override @Override
public String toString() { public String toString() {
return Integer.toString(mId); return Integer.toString(mId);