Merge "Merge remote-tracking branch 'goog/master' into mergescriptpackage" into jb-ub-latinimegoogle
commit
b58ae231de
|
@ -34,6 +34,7 @@
|
||||||
<!--
|
<!--
|
||||||
Configuration for LatinKeyboardView
|
Configuration for LatinKeyboardView
|
||||||
-->
|
-->
|
||||||
|
<dimen name="config_key_hysteresis_distance">40.0dp</dimen>
|
||||||
<bool name="config_sliding_key_input_enabled">false</bool>
|
<bool name="config_sliding_key_input_enabled">false</bool>
|
||||||
<!-- Showing more keys keyboard, just above the touched point if true, aligned to the key if
|
<!-- Showing more keys keyboard, just above the touched point if true, aligned to the key if
|
||||||
false -->
|
false -->
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
<Key
|
<Key
|
||||||
latin:keyLabel="." />
|
latin:keyLabel="." />
|
||||||
<Key
|
<Key
|
||||||
latin:keyLabel="!text/keylabel_for_symbols_exclamation"
|
latin:keyLabel="!"
|
||||||
latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
|
latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
|
||||||
<Key
|
<Key
|
||||||
latin:keyLabel="!text/keylabel_for_symbols_question"
|
latin:keyLabel="!text/keylabel_for_symbols_question"
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
|
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
|
||||||
>
|
>
|
||||||
<Key
|
<Key
|
||||||
latin:keyLabel="!text/keylabel_for_symbols_exclamation"
|
latin:keyLabel="!"
|
||||||
latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
|
latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
|
||||||
<switch>
|
<switch>
|
||||||
<case
|
<case
|
||||||
|
|
|
@ -87,7 +87,6 @@ public class Keyboard {
|
||||||
public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
|
public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
|
||||||
public static final int CODE_CLOSING_CURLY_BRACKET = '}';
|
public static final int CODE_CLOSING_CURLY_BRACKET = '}';
|
||||||
public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
|
public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
|
||||||
private static final int MINIMUM_LETTER_CODE = CODE_TAB;
|
|
||||||
|
|
||||||
/** Special keys code. Must be negative.
|
/** Special keys code. Must be negative.
|
||||||
* These should be aligned with KeyboardCodesSet.ID_TO_NAME[],
|
* These should be aligned with KeyboardCodesSet.ID_TO_NAME[],
|
||||||
|
@ -217,7 +216,7 @@ public class Keyboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isLetterCode(int code) {
|
public static boolean isLetterCode(int code) {
|
||||||
return code >= MINIMUM_LETTER_CODE;
|
return code >= CODE_SPACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Params {
|
public static class Params {
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
|
|
||||||
package com.android.inputmethod.keyboard;
|
package com.android.inputmethod.keyboard;
|
||||||
|
|
||||||
|
import com.android.inputmethod.latin.InputPointers;
|
||||||
|
import com.android.inputmethod.latin.SuggestedWords;
|
||||||
|
|
||||||
public interface KeyboardActionListener {
|
public interface KeyboardActionListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,13 +67,18 @@ public interface KeyboardActionListener {
|
||||||
*/
|
*/
|
||||||
public void onTextInput(CharSequence text);
|
public void onTextInput(CharSequence text);
|
||||||
|
|
||||||
// TODO: Should move this method to some more appropriate interface.
|
|
||||||
/**
|
/**
|
||||||
* Called when user started batch input.
|
* Called when user started batch input.
|
||||||
*/
|
*/
|
||||||
public void onStartBatchInput();
|
public void onStartBatchInput();
|
||||||
|
|
||||||
// TODO: Should move this method to some more appropriate interface.
|
/**
|
||||||
|
* Sends the batch input points data to get updated suggestions
|
||||||
|
* @param batchPointers the batch input points representing the user input
|
||||||
|
* @return updated suggestions that reflects the user input
|
||||||
|
*/
|
||||||
|
public SuggestedWords onUpdateBatchInput(InputPointers batchPointers);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a sequence of characters to the listener as batch input.
|
* Sends a sequence of characters to the listener as batch input.
|
||||||
*
|
*
|
||||||
|
@ -101,6 +109,8 @@ public interface KeyboardActionListener {
|
||||||
@Override
|
@Override
|
||||||
public void onStartBatchInput() {}
|
public void onStartBatchInput() {}
|
||||||
@Override
|
@Override
|
||||||
|
public SuggestedWords onUpdateBatchInput(InputPointers batchPointers) { return null; }
|
||||||
|
@Override
|
||||||
public void onEndBatchInput(CharSequence text) {}
|
public void onEndBatchInput(CharSequence text) {}
|
||||||
@Override
|
@Override
|
||||||
public void onCancelInput() {}
|
public void onCancelInput() {}
|
||||||
|
|
|
@ -22,6 +22,7 @@ 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.GestureTracker;
|
||||||
import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
|
import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
|
||||||
import com.android.inputmethod.latin.LatinImeLogger;
|
import com.android.inputmethod.latin.LatinImeLogger;
|
||||||
import com.android.inputmethod.latin.ResearchLogger;
|
import com.android.inputmethod.latin.ResearchLogger;
|
||||||
|
@ -161,6 +162,9 @@ 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();
|
||||||
|
|
||||||
public static void init(boolean hasDistinctMultitouch,
|
public static void init(boolean hasDistinctMultitouch,
|
||||||
boolean needsPhantomSuddenMoveEventHack) {
|
boolean needsPhantomSuddenMoveEventHack) {
|
||||||
if (hasDistinctMultitouch) {
|
if (hasDistinctMultitouch) {
|
||||||
|
@ -199,6 +203,7 @@ 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) {
|
||||||
|
@ -207,6 +212,7 @@ 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() {
|
||||||
|
@ -233,6 +239,9 @@ public class PointerTracker {
|
||||||
|
|
||||||
// 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()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
|
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
|
||||||
if (DEBUG_LISTENER) {
|
if (DEBUG_LISTENER) {
|
||||||
Log.d(TAG, "onPress : " + KeyDetector.printableCode(key)
|
Log.d(TAG, "onPress : " + KeyDetector.printableCode(key)
|
||||||
|
@ -286,6 +295,9 @@ 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()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
|
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
|
||||||
if (DEBUG_LISTENER) {
|
if (DEBUG_LISTENER) {
|
||||||
Log.d(TAG, "onRelease : " + Keyboard.printableCode(primaryCode)
|
Log.d(TAG, "onRelease : " + Keyboard.printableCode(primaryCode)
|
||||||
|
@ -386,7 +398,7 @@ public class PointerTracker {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!key.noKeyPreview()) {
|
if (!key.noKeyPreview() && !sGestureTracker.isInGesture()) {
|
||||||
mDrawingProxy.showKeyPreview(this);
|
mDrawingProxy.showKeyPreview(this);
|
||||||
}
|
}
|
||||||
updatePressKeyGraphics(key);
|
updatePressKeyGraphics(key);
|
||||||
|
@ -504,8 +516,8 @@ public class PointerTracker {
|
||||||
}
|
}
|
||||||
|
|
||||||
final PointerTrackerQueue queue = sPointerTrackerQueue;
|
final PointerTrackerQueue queue = sPointerTrackerQueue;
|
||||||
if (queue != null) {
|
|
||||||
final Key key = getKeyOn(x, y);
|
final Key key = getKeyOn(x, y);
|
||||||
|
if (queue != null) {
|
||||||
if (key != null && key.isModifier()) {
|
if (key != null && key.isModifier()) {
|
||||||
// Before processing a down event of modifier key, all pointers already being
|
// Before processing a down event of modifier key, all pointers already being
|
||||||
// tracked should be released.
|
// tracked should be released.
|
||||||
|
@ -514,6 +526,9 @@ public class PointerTracker {
|
||||||
queue.add(this);
|
queue.add(this);
|
||||||
}
|
}
|
||||||
onDownEventInternal(x, y, eventTime);
|
onDownEventInternal(x, y, eventTime);
|
||||||
|
if (queue != null && queue.size() == 1) {
|
||||||
|
sGestureTracker.onDownEvent(this, x, y, eventTime, key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDownEventInternal(int x, int y, long eventTime) {
|
private void onDownEventInternal(int x, int y, long eventTime) {
|
||||||
|
@ -554,10 +569,34 @@ public class PointerTracker {
|
||||||
if (mKeyAlreadyProcessed)
|
if (mKeyAlreadyProcessed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (me != null) {
|
||||||
|
// Add historical points to gesture path.
|
||||||
|
final int pointerIndex = me.findPointerIndex(mPointerId);
|
||||||
|
final int historicalSize = me.getHistorySize();
|
||||||
|
for (int h = 0; h < historicalSize; h++) {
|
||||||
|
final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
|
||||||
|
final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
|
||||||
|
final long historicalTime = me.getHistoricalEventTime(h);
|
||||||
|
sGestureTracker.onMoveEvent(this, historicalX, historicalY, historicalTime,
|
||||||
|
true /* isHistorical */, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final int lastX = mLastX;
|
final int lastX = mLastX;
|
||||||
final int lastY = mLastY;
|
final int lastY = mLastY;
|
||||||
final Key oldKey = mCurrentKey;
|
final Key oldKey = mCurrentKey;
|
||||||
Key key = onMoveKey(x, y);
|
Key key = onMoveKey(x, y);
|
||||||
|
|
||||||
|
// Register move event on gesture tracker.
|
||||||
|
sGestureTracker.onMoveEvent(this, x, y, eventTime, false, key);
|
||||||
|
if (sGestureTracker.isInGesture()) {
|
||||||
|
mIgnoreModifierKey = true;
|
||||||
|
mTimerProxy.cancelLongPressTimer();
|
||||||
|
mIsInSlidingKeyInput = true;
|
||||||
|
mCurrentKey = null;
|
||||||
|
setReleasedKeyGraphics(oldKey);
|
||||||
|
}
|
||||||
|
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
if (oldKey == null) {
|
if (oldKey == null) {
|
||||||
// The pointer has been slid in to the new key, but the finger was not on any keys.
|
// The pointer has been slid in to the new key, but the finger was not on any keys.
|
||||||
|
@ -607,7 +646,7 @@ public class PointerTracker {
|
||||||
if (ProductionFlag.IS_EXPERIMENTAL) {
|
if (ProductionFlag.IS_EXPERIMENTAL) {
|
||||||
ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
|
ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
|
||||||
}
|
}
|
||||||
onUpEventInternal();
|
onUpEventInternal(x, y, eventTime);
|
||||||
onDownEventInternal(x, y, eventTime);
|
onDownEventInternal(x, y, eventTime);
|
||||||
} else {
|
} else {
|
||||||
// HACK: If there are currently multiple touches, register the key even if
|
// HACK: If there are currently multiple touches, register the key even if
|
||||||
|
@ -617,7 +656,7 @@ public class PointerTracker {
|
||||||
// this hack.
|
// this hack.
|
||||||
if (me != null && me.getPointerCount() > 1
|
if (me != null && me.getPointerCount() > 1
|
||||||
&& !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
|
&& !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
|
||||||
onUpEventInternal();
|
onUpEventInternal(x, y, eventTime);
|
||||||
}
|
}
|
||||||
mKeyAlreadyProcessed = true;
|
mKeyAlreadyProcessed = true;
|
||||||
setReleasedKeyGraphics(oldKey);
|
setReleasedKeyGraphics(oldKey);
|
||||||
|
@ -647,6 +686,7 @@ public class PointerTracker {
|
||||||
|
|
||||||
final PointerTrackerQueue queue = sPointerTrackerQueue;
|
final PointerTrackerQueue queue = sPointerTrackerQueue;
|
||||||
if (queue != null) {
|
if (queue != null) {
|
||||||
|
if (!sGestureTracker.isInGesture()) {
|
||||||
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.
|
||||||
|
@ -654,9 +694,10 @@ public class PointerTracker {
|
||||||
} else {
|
} else {
|
||||||
queue.releaseAllPointersOlderThan(this, eventTime);
|
queue.releaseAllPointersOlderThan(this, eventTime);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
queue.remove(this);
|
queue.remove(this);
|
||||||
}
|
}
|
||||||
onUpEventInternal();
|
onUpEventInternal(x, y, eventTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
|
// Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
|
||||||
|
@ -665,11 +706,11 @@ public class PointerTracker {
|
||||||
public void onPhantomUpEvent(int x, int y, long eventTime) {
|
public void onPhantomUpEvent(int x, int y, long eventTime) {
|
||||||
if (DEBUG_EVENT)
|
if (DEBUG_EVENT)
|
||||||
printTouchEvent("onPhntEvent:", x, y, eventTime);
|
printTouchEvent("onPhntEvent:", x, y, eventTime);
|
||||||
onUpEventInternal();
|
onUpEventInternal(x, y, eventTime);
|
||||||
mKeyAlreadyProcessed = true;
|
mKeyAlreadyProcessed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onUpEventInternal() {
|
private void onUpEventInternal(int x, int y, long eventTime) {
|
||||||
mTimerProxy.cancelKeyTimers();
|
mTimerProxy.cancelKeyTimers();
|
||||||
mIsInSlidingKeyInput = false;
|
mIsInSlidingKeyInput = false;
|
||||||
// Release the last pressed key.
|
// Release the last pressed key.
|
||||||
|
@ -678,6 +719,24 @@ public class PointerTracker {
|
||||||
mDrawingProxy.dismissMoreKeysPanel();
|
mDrawingProxy.dismissMoreKeysPanel();
|
||||||
mIsShowingMoreKeysPanel = false;
|
mIsShowingMoreKeysPanel = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sGestureTracker.isInGesture()) {
|
||||||
|
// Register up event on gesture tracker.
|
||||||
|
sGestureTracker.onUpEvent(this, x, y, eventTime);
|
||||||
|
if (!sPointerTrackerQueue.isAnyInSlidingKeyInput()) {
|
||||||
|
// TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code.
|
||||||
|
sGestureTracker.endBatchInput();
|
||||||
|
}
|
||||||
|
if (mCurrentKey != null) {
|
||||||
|
callListenerOnRelease(mCurrentKey, mCurrentKey.mCode, true);
|
||||||
|
}
|
||||||
|
mCurrentKey = null;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code.
|
||||||
|
sGestureTracker.endBatchInput();
|
||||||
|
}
|
||||||
|
|
||||||
if (mKeyAlreadyProcessed)
|
if (mKeyAlreadyProcessed)
|
||||||
return;
|
return;
|
||||||
if (mCurrentKey != null && !mCurrentKey.isRepeatable()) {
|
if (mCurrentKey != null && !mCurrentKey.isRepeatable()) {
|
||||||
|
@ -689,6 +748,8 @@ 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.
|
||||||
|
sGestureTracker.abortBatchInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onLongPressed() {
|
public void onLongPressed() {
|
||||||
|
@ -723,7 +784,7 @@ public class PointerTracker {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startRepeatKey(Key key) {
|
private void startRepeatKey(Key key) {
|
||||||
if (key != null && key.isRepeatable()) {
|
if (key != null && key.isRepeatable() && !sGestureTracker.isInGesture()) {
|
||||||
onRegisterKey(key);
|
onRegisterKey(key);
|
||||||
mTimerProxy.startKeyRepeatTimer(this);
|
mTimerProxy.startKeyRepeatTimer(this);
|
||||||
}
|
}
|
||||||
|
@ -753,7 +814,7 @@ public class PointerTracker {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLongPressTimer(Key key) {
|
private void startLongPressTimer(Key key) {
|
||||||
if (key != null && key.isLongPressEnabled()) {
|
if (key != null && key.isLongPressEnabled() && !sGestureTracker.isInGesture()) {
|
||||||
mTimerProxy.startLongPressTimer(this);
|
mTimerProxy.startLongPressTimer(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,326 @@
|
||||||
|
/*
|
||||||
|
* 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 android.util.SparseArray;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import com.android.inputmethod.latin.SuggestedWords;
|
||||||
|
|
||||||
|
// 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 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;
|
||||||
|
private boolean mInGesture = false;
|
||||||
|
|
||||||
|
private KeyboardActionListener mListener;
|
||||||
|
private SuggestedWords mSuggestions;
|
||||||
|
|
||||||
|
private final SparseArray<GestureStroke> mGestureStrokes = new SparseArray<GestureStroke>();
|
||||||
|
|
||||||
|
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();
|
||||||
|
GestureStroke.setGestureSampleLength(keyboard.mMostCommonKeyWidth / 2,
|
||||||
|
keyboard.mMostCommonKeyHeight / 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startBatchInput() {
|
||||||
|
if (DEBUG_LISTENER) {
|
||||||
|
Log.d(TAG, "onStartBatchInput");
|
||||||
|
}
|
||||||
|
mInGesture = true;
|
||||||
|
mListener.onStartBatchInput();
|
||||||
|
mSuggestions = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: The corresponding startBatchInput() is a private method. Reorganize the code.
|
||||||
|
public void endBatchInput() {
|
||||||
|
if (isInGesture() && mSuggestions != null && mSuggestions.size() > 0) {
|
||||||
|
final CharSequence text = mSuggestions.getWord(0);
|
||||||
|
if (DEBUG_LISTENER) {
|
||||||
|
Log.d(TAG, "onEndBatchInput: text=" + text);
|
||||||
|
}
|
||||||
|
mListener.onEndBatchInput(text);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
addPointToStroke(x, y, 0, tracker.mPointerId, 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 = addPointToStroke(x, y, gestureTime, tracker.mPointerId,
|
||||||
|
isHistorical);
|
||||||
|
if (!isInGesture() && stroke.isStartOfAGesture(gestureTime)) {
|
||||||
|
startBatchInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key != null && isInGesture()) {
|
||||||
|
final InputPointers batchPoints = getIncrementalBatchPoints();
|
||||||
|
if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
|
||||||
|
if (DEBUG_LISTENER) {
|
||||||
|
Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
|
||||||
|
}
|
||||||
|
mSuggestions = mListener.onUpdateBatchInput(batchPoints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onUpEvent(PointerTracker tracker, int x, int y, long eventTime) {
|
||||||
|
if (isInGesture()) {
|
||||||
|
final InputPointers batchPoints = getAllBatchPoints();
|
||||||
|
if (DEBUG_LISTENER) {
|
||||||
|
Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
|
||||||
|
}
|
||||||
|
mSuggestions = mListener.onUpdateBatchInput(batchPoints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -195,38 +195,37 @@ public final class KeyboardTextsSet {
|
||||||
/* 87 */ "more_keys_for_symbols_0",
|
/* 87 */ "more_keys_for_symbols_0",
|
||||||
/* 88 */ "keylabel_for_comma",
|
/* 88 */ "keylabel_for_comma",
|
||||||
/* 89 */ "more_keys_for_comma",
|
/* 89 */ "more_keys_for_comma",
|
||||||
/* 90 */ "keylabel_for_symbols_exclamation",
|
/* 90 */ "keylabel_for_symbols_question",
|
||||||
/* 91 */ "keylabel_for_symbols_question",
|
/* 91 */ "keylabel_for_symbols_semicolon",
|
||||||
/* 92 */ "keylabel_for_symbols_semicolon",
|
/* 92 */ "keylabel_for_symbols_percent",
|
||||||
/* 93 */ "keylabel_for_symbols_percent",
|
/* 93 */ "more_keys_for_symbols_exclamation",
|
||||||
/* 94 */ "more_keys_for_symbols_exclamation",
|
/* 94 */ "more_keys_for_symbols_question",
|
||||||
/* 95 */ "more_keys_for_symbols_question",
|
/* 95 */ "more_keys_for_symbols_semicolon",
|
||||||
/* 96 */ "more_keys_for_symbols_semicolon",
|
/* 96 */ "more_keys_for_symbols_percent",
|
||||||
/* 97 */ "more_keys_for_symbols_percent",
|
/* 97 */ "keylabel_for_tablet_comma",
|
||||||
/* 98 */ "keylabel_for_tablet_comma",
|
/* 98 */ "keyhintlabel_for_tablet_comma",
|
||||||
/* 99 */ "keyhintlabel_for_tablet_comma",
|
/* 99 */ "more_keys_for_tablet_comma",
|
||||||
/* 100 */ "more_keys_for_tablet_comma",
|
/* 100 */ "keyhintlabel_for_tablet_period",
|
||||||
/* 101 */ "keyhintlabel_for_tablet_period",
|
/* 101 */ "more_keys_for_tablet_period",
|
||||||
/* 102 */ "more_keys_for_tablet_period",
|
/* 102 */ "keylabel_for_apostrophe",
|
||||||
/* 103 */ "keylabel_for_apostrophe",
|
/* 103 */ "keyhintlabel_for_apostrophe",
|
||||||
/* 104 */ "keyhintlabel_for_apostrophe",
|
/* 104 */ "more_keys_for_apostrophe",
|
||||||
/* 105 */ "more_keys_for_apostrophe",
|
/* 105 */ "more_keys_for_am_pm",
|
||||||
/* 106 */ "more_keys_for_am_pm",
|
/* 106 */ "settings_as_more_key",
|
||||||
/* 107 */ "settings_as_more_key",
|
/* 107 */ "shortcut_as_more_key",
|
||||||
/* 108 */ "shortcut_as_more_key",
|
/* 108 */ "action_next_as_more_key",
|
||||||
/* 109 */ "action_next_as_more_key",
|
/* 109 */ "action_previous_as_more_key",
|
||||||
/* 110 */ "action_previous_as_more_key",
|
/* 110 */ "label_to_more_symbol_key",
|
||||||
/* 111 */ "label_to_more_symbol_key",
|
/* 111 */ "label_to_more_symbol_for_tablet_key",
|
||||||
/* 112 */ "label_to_more_symbol_for_tablet_key",
|
/* 112 */ "label_tab_key",
|
||||||
/* 113 */ "label_tab_key",
|
/* 113 */ "label_to_phone_numeric_key",
|
||||||
/* 114 */ "label_to_phone_numeric_key",
|
/* 114 */ "label_to_phone_symbols_key",
|
||||||
/* 115 */ "label_to_phone_symbols_key",
|
/* 115 */ "label_time_am",
|
||||||
/* 116 */ "label_time_am",
|
/* 116 */ "label_time_pm",
|
||||||
/* 117 */ "label_time_pm",
|
/* 117 */ "label_to_symbol_key_pcqwerty",
|
||||||
/* 118 */ "label_to_symbol_key_pcqwerty",
|
/* 118 */ "keylabel_for_popular_domain",
|
||||||
/* 119 */ "keylabel_for_popular_domain",
|
/* 119 */ "more_keys_for_popular_domain",
|
||||||
/* 120 */ "more_keys_for_popular_domain",
|
/* 120 */ "more_keys_for_smiley",
|
||||||
/* 121 */ "more_keys_for_smiley",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final String EMPTY = "";
|
private static final String EMPTY = "";
|
||||||
|
@ -331,52 +330,51 @@ public final class KeyboardTextsSet {
|
||||||
/* 87 */ "\u207F,\u2205",
|
/* 87 */ "\u207F,\u2205",
|
||||||
/* 88 */ ",",
|
/* 88 */ ",",
|
||||||
/* 89 */ EMPTY,
|
/* 89 */ EMPTY,
|
||||||
/* 90 */ "!",
|
/* 90 */ "?",
|
||||||
/* 91 */ "?",
|
/* 91 */ ";",
|
||||||
/* 92 */ ";",
|
/* 92 */ "%",
|
||||||
/* 93 */ "%",
|
|
||||||
// U+00A1: "¡" INVERTED EXCLAMATION MARK
|
// U+00A1: "¡" INVERTED EXCLAMATION MARK
|
||||||
/* 94 */ "\u00A1",
|
/* 93 */ "\u00A1",
|
||||||
// U+00BF: "¿" INVERTED QUESTION MARK
|
// U+00BF: "¿" INVERTED QUESTION MARK
|
||||||
/* 95 */ "\u00BF",
|
/* 94 */ "\u00BF",
|
||||||
/* 96 */ EMPTY,
|
/* 95 */ EMPTY,
|
||||||
// U+2030: "‰" PER MILLE SIGN
|
// U+2030: "‰" PER MILLE SIGN
|
||||||
/* 97 */ "\u2030",
|
/* 96 */ "\u2030",
|
||||||
/* 98 */ ",",
|
/* 97 */ ",",
|
||||||
|
/* 98 */ "!",
|
||||||
/* 99 */ "!",
|
/* 99 */ "!",
|
||||||
/* 100 */ "!",
|
/* 100 */ "?",
|
||||||
/* 101 */ "?",
|
/* 101 */ "?",
|
||||||
/* 102 */ "?",
|
/* 102 */ "\'",
|
||||||
/* 103 */ "\'",
|
/* 103 */ "\"",
|
||||||
/* 104 */ "\"",
|
/* 104 */ "\"",
|
||||||
/* 105 */ "\"",
|
/* 105 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm",
|
||||||
/* 106 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm",
|
/* 106 */ "!icon/settings_key|!code/key_settings",
|
||||||
/* 107 */ "!icon/settings_key|!code/key_settings",
|
/* 107 */ "!icon/shortcut_key|!code/key_shortcut",
|
||||||
/* 108 */ "!icon/shortcut_key|!code/key_shortcut",
|
/* 108 */ "!hasLabels!,!text/label_next_key|!code/key_action_next",
|
||||||
/* 109 */ "!hasLabels!,!text/label_next_key|!code/key_action_next",
|
/* 109 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous",
|
||||||
/* 110 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous",
|
|
||||||
// Label for "switch to more symbol" modifier key. Must be short to fit on key!
|
// Label for "switch to more symbol" modifier key. Must be short to fit on key!
|
||||||
/* 111 */ "= \\ <",
|
/* 110 */ "= \\ <",
|
||||||
// Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key!
|
// Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key!
|
||||||
/* 112 */ "~ \\ {",
|
/* 111 */ "~ \\ {",
|
||||||
// Label for "Tab" key. Must be short to fit on key!
|
// Label for "Tab" key. Must be short to fit on key!
|
||||||
/* 113 */ "Tab",
|
/* 112 */ "Tab",
|
||||||
// Label for "switch to phone numeric" key. Must be short to fit on key!
|
// Label for "switch to phone numeric" key. Must be short to fit on key!
|
||||||
/* 114 */ "123",
|
/* 113 */ "123",
|
||||||
// Label for "switch to phone symbols" key. Must be short to fit on key!
|
// Label for "switch to phone symbols" key. Must be short to fit on key!
|
||||||
// U+FF0A: "*" FULLWIDTH ASTERISK
|
// U+FF0A: "*" FULLWIDTH ASTERISK
|
||||||
// U+FF03: "#" FULLWIDTH NUMBER SIGN
|
// U+FF03: "#" FULLWIDTH NUMBER SIGN
|
||||||
/* 115 */ "\uFF0A\uFF03",
|
/* 114 */ "\uFF0A\uFF03",
|
||||||
// Key label for "ante meridiem"
|
// Key label for "ante meridiem"
|
||||||
/* 116 */ "AM",
|
/* 115 */ "AM",
|
||||||
// Key label for "post meridiem"
|
// Key label for "post meridiem"
|
||||||
/* 117 */ "PM",
|
/* 116 */ "PM",
|
||||||
// Label for "switch to symbols" key on PC QWERTY layout
|
// Label for "switch to symbols" key on PC QWERTY layout
|
||||||
/* 118 */ "Sym",
|
/* 117 */ "Sym",
|
||||||
/* 119 */ ".com",
|
/* 118 */ ".com",
|
||||||
// popular web domains for the locale - most popular, displayed on the keyboard
|
// popular web domains for the locale - most popular, displayed on the keyboard
|
||||||
/* 120 */ "!hasLabels!,.net,.org,.gov,.edu",
|
/* 119 */ "!hasLabels!,.net,.org,.gov,.edu",
|
||||||
/* 121 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ",
|
/* 120 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ",
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Language ar: Arabic */
|
/* Language ar: Arabic */
|
||||||
|
@ -483,25 +481,24 @@ public final class KeyboardTextsSet {
|
||||||
// U+060C: "،" ARABIC COMMA
|
// U+060C: "،" ARABIC COMMA
|
||||||
/* 88 */ "\u060C",
|
/* 88 */ "\u060C",
|
||||||
/* 89 */ "\\,",
|
/* 89 */ "\\,",
|
||||||
/* 90 */ null,
|
/* 90 */ "\u061F",
|
||||||
/* 91 */ "\u061F",
|
/* 91 */ "\u061B",
|
||||||
/* 92 */ "\u061B",
|
|
||||||
// U+066A: "٪" ARABIC PERCENT SIGN
|
// U+066A: "٪" ARABIC PERCENT SIGN
|
||||||
/* 93 */ "\u066A",
|
/* 92 */ "\u066A",
|
||||||
/* 94 */ null,
|
/* 93 */ null,
|
||||||
/* 95 */ "?",
|
/* 94 */ "?",
|
||||||
/* 96 */ ";",
|
/* 95 */ ";",
|
||||||
// U+2030: "‰" PER MILLE SIGN
|
// U+2030: "‰" PER MILLE SIGN
|
||||||
/* 97 */ "\\%,\u2030",
|
/* 96 */ "\\%,\u2030",
|
||||||
/* 98~ */
|
/* 97~ */
|
||||||
null, null, null, null, null,
|
null, null, null, null, null,
|
||||||
/* ~102 */
|
/* ~101 */
|
||||||
// U+060C: "،" ARABIC COMMA
|
// U+060C: "،" ARABIC COMMA
|
||||||
// U+061B: "؛" ARABIC SEMICOLON
|
// U+061B: "؛" ARABIC SEMICOLON
|
||||||
// U+061F: "؟" ARABIC QUESTION MARK
|
// U+061F: "؟" ARABIC QUESTION MARK
|
||||||
/* 103 */ "\u060C",
|
/* 102 */ "\u060C",
|
||||||
/* 104 */ "\u061F",
|
/* 103 */ "\u061F",
|
||||||
/* 105 */ "\u061F,\u061B,!,:,-,/,\',\"",
|
/* 104 */ "\u061F,\u061B,!,:,-,/,\',\"",
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Language be: Belarusian */
|
/* Language be: Belarusian */
|
||||||
|
@ -861,27 +858,18 @@ public final class KeyboardTextsSet {
|
||||||
/* ~47 */
|
/* ~47 */
|
||||||
// U+00A1: "¡" INVERTED EXCLAMATION MARK
|
// U+00A1: "¡" INVERTED EXCLAMATION MARK
|
||||||
// U+00BF: "¿" INVERTED QUESTION MARK
|
// U+00BF: "¿" INVERTED QUESTION MARK
|
||||||
/* 48 */ "!fixedColumnOrder!9,\",\',#,-,\u00A1,!,\u00BF,\\,,?,@,&,\\%,+,;,:,/,(,)",
|
/* 48 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)",
|
||||||
/* 49~ */
|
/* 49~ */
|
||||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
|
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
|
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||||
null, null, null, null, null, null, null, null, null, null, null,
|
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||||
/* ~89 */
|
null, null, null, null, null,
|
||||||
// U+00A1: "¡" INVERTED EXCLAMATION MARK
|
|
||||||
/* 90 */ "\u00A1",
|
|
||||||
// U+00BF: "¿" INVERTED QUESTION MARK
|
|
||||||
/* 91 */ "\u00BF",
|
|
||||||
/* 92 */ null,
|
|
||||||
/* 93 */ null,
|
|
||||||
/* 94 */ "!",
|
|
||||||
/* 95 */ "?",
|
|
||||||
/* 96~ */
|
|
||||||
null, null, null,
|
|
||||||
/* ~98 */
|
/* ~98 */
|
||||||
/* 99 */ "\u00A1",
|
// U+00A1: "¡" INVERTED EXCLAMATION MARK
|
||||||
/* 100 */ "\u00A1,!",
|
/* 99 */ "!,\u00A1",
|
||||||
/* 101 */ "\u00BF",
|
/* 100 */ null,
|
||||||
/* 102 */ "\u00BF,?",
|
// U+00BF: "¿" INVERTED QUESTION MARK
|
||||||
|
/* 101 */ "?,\u00BF",
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Language et: Estonian */
|
/* Language et: Estonian */
|
||||||
|
@ -1088,29 +1076,28 @@ public final class KeyboardTextsSet {
|
||||||
// U+060C: "،" ARABIC COMMA
|
// U+060C: "،" ARABIC COMMA
|
||||||
/* 88 */ "\u060C",
|
/* 88 */ "\u060C",
|
||||||
/* 89 */ "\\,",
|
/* 89 */ "\\,",
|
||||||
/* 90 */ null,
|
/* 90 */ "\u061F",
|
||||||
/* 91 */ "\u061F",
|
/* 91 */ "\u061B",
|
||||||
/* 92 */ "\u061B",
|
|
||||||
// U+066A: "٪" ARABIC PERCENT SIGN
|
// U+066A: "٪" ARABIC PERCENT SIGN
|
||||||
/* 93 */ "\u066A",
|
/* 92 */ "\u066A",
|
||||||
/* 94 */ null,
|
/* 93 */ null,
|
||||||
/* 95 */ "?",
|
/* 94 */ "?",
|
||||||
/* 96 */ ";",
|
/* 95 */ ";",
|
||||||
// U+2030: "‰" PER MILLE SIGN
|
// U+2030: "‰" PER MILLE SIGN
|
||||||
/* 97 */ "\\%,\u2030",
|
/* 96 */ "\\%,\u2030",
|
||||||
// U+060C: "،" ARABIC COMMA
|
// U+060C: "،" ARABIC COMMA
|
||||||
// U+061B: "؛" ARABIC SEMICOLON
|
// U+061B: "؛" ARABIC SEMICOLON
|
||||||
// U+061F: "؟" ARABIC QUESTION MARK
|
// U+061F: "؟" ARABIC QUESTION MARK
|
||||||
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
|
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
|
||||||
// U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
|
// U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
|
||||||
/* 98 */ "\u060C",
|
/* 97 */ "\u060C",
|
||||||
/* 99 */ "!",
|
/* 98 */ "!",
|
||||||
/* 100 */ "!,\\,",
|
/* 99 */ "!,\\,",
|
||||||
/* 101 */ "\u061F",
|
/* 100 */ "\u061F",
|
||||||
/* 102 */ "\u061F,?",
|
/* 101 */ "\u061F,?",
|
||||||
/* 103 */ "\u060C",
|
/* 102 */ "\u060C",
|
||||||
/* 104 */ "\u061F",
|
/* 103 */ "\u061F",
|
||||||
/* 105 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB",
|
/* 104 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB",
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Language fi: Finnish */
|
/* Language fi: Finnish */
|
||||||
|
|
|
@ -31,6 +31,10 @@ public class PointerTrackerQueue {
|
||||||
// TODO: Use ring buffer instead of {@link LinkedList}.
|
// TODO: Use ring buffer instead of {@link LinkedList}.
|
||||||
private final LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>();
|
private final LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>();
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return mQueue.size();
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void add(PointerTracker tracker) {
|
public synchronized void add(PointerTracker tracker) {
|
||||||
mQueue.add(tracker);
|
mQueue.add(tracker);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1268,13 +1268,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
if (SPACE_STATE_PHANTOM == spaceState) {
|
if (SPACE_STATE_PHANTOM == spaceState) {
|
||||||
commitTyped(LastComposedWord.NOT_A_SEPARATOR);
|
commitTyped(LastComposedWord.NOT_A_SEPARATOR);
|
||||||
}
|
}
|
||||||
|
final int keyX, keyY;
|
||||||
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
|
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
|
||||||
if (keyboard != null && keyboard.hasProximityCharsCorrection(primaryCode)) {
|
if (keyboard != null && keyboard.hasProximityCharsCorrection(primaryCode)) {
|
||||||
handleCharacter(primaryCode, x, y, spaceState);
|
keyX = x;
|
||||||
|
keyY = y;
|
||||||
} else {
|
} else {
|
||||||
handleCharacter(primaryCode, NOT_A_TOUCH_COORDINATE, NOT_A_TOUCH_COORDINATE,
|
keyX = NOT_A_TOUCH_COORDINATE;
|
||||||
spaceState);
|
keyY = NOT_A_TOUCH_COORDINATE;
|
||||||
}
|
}
|
||||||
|
handleCharacter(primaryCode, keyX, keyY, spaceState);
|
||||||
}
|
}
|
||||||
mExpectingUpdateSelection = true;
|
mExpectingUpdateSelection = true;
|
||||||
mShouldSwitchToLastSubtype = true;
|
mShouldSwitchToLastSubtype = true;
|
||||||
|
@ -1320,10 +1323,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
mSpaceState = SPACE_STATE_PHANTOM;
|
mSpaceState = SPACE_STATE_PHANTOM;
|
||||||
}
|
}
|
||||||
mConnection.endBatchEdit();
|
mConnection.endBatchEdit();
|
||||||
|
// TODO: Should handle TextUtils.CAP_MODE_CHARACTER.
|
||||||
|
mWordComposer.setAutoCapitalized(
|
||||||
|
getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SuggestedWords onUpdateBatchInput(InputPointers batchPointers) {
|
||||||
|
mWordComposer.setBatchInputPointers(batchPointers);
|
||||||
|
return updateSuggestionsOrPredictions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEndBatchInput(CharSequence text) {
|
public void onEndBatchInput(CharSequence text) {
|
||||||
|
mWordComposer.setBatchInputWord(text);
|
||||||
mConnection.beginBatchEdit();
|
mConnection.beginBatchEdit();
|
||||||
if (SPACE_STATE_PHANTOM == mSpaceState) {
|
if (SPACE_STATE_PHANTOM == mSpaceState) {
|
||||||
sendKeyCodePoint(Keyboard.CODE_SPACE);
|
sendKeyCodePoint(Keyboard.CODE_SPACE);
|
||||||
|
@ -1669,7 +1682,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: rename this method to updateSuggestionStrip or simply updateSuggestions
|
// TODO: rename this method to updateSuggestionStrip or simply updateSuggestions
|
||||||
private void updateSuggestionsOrPredictions() {
|
private SuggestedWords updateSuggestionsOrPredictions() {
|
||||||
mHandler.cancelUpdateSuggestionStrip();
|
mHandler.cancelUpdateSuggestionStrip();
|
||||||
|
|
||||||
// Check if we have a suggestion engine attached.
|
// Check if we have a suggestion engine attached.
|
||||||
|
@ -1679,13 +1692,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
+ "requested!");
|
+ "requested!");
|
||||||
mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
|
mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
|
||||||
}
|
}
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String typedWord = mWordComposer.getTypedWord();
|
final String typedWord = mWordComposer.getTypedWord();
|
||||||
if (!mWordComposer.isComposingWord() && !mCurrentSettings.mBigramPredictionEnabled) {
|
if (!mWordComposer.isComposingWord() && !mCurrentSettings.mBigramPredictionEnabled) {
|
||||||
setPunctuationSuggestions();
|
setPunctuationSuggestions();
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the word on which we should search the bigrams. If we are composing a word, it's
|
// Get the word on which we should search the bigrams. If we are composing a word, it's
|
||||||
|
@ -1701,6 +1714,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
suggestedWords = maybeRetrieveOlderSuggestions(typedWord, suggestedWords);
|
suggestedWords = maybeRetrieveOlderSuggestions(typedWord, suggestedWords);
|
||||||
|
|
||||||
showSuggestions(suggestedWords, typedWord);
|
showSuggestions(suggestedWords, typedWord);
|
||||||
|
return suggestedWords;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SuggestedWords maybeRetrieveOlderSuggestions(final CharSequence typedWord,
|
private SuggestedWords maybeRetrieveOlderSuggestions(final CharSequence typedWord,
|
||||||
|
@ -1761,9 +1775,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
if (mHandler.hasPendingUpdateSuggestions()) {
|
if (mHandler.hasPendingUpdateSuggestions()) {
|
||||||
updateSuggestionsOrPredictions();
|
updateSuggestionsOrPredictions();
|
||||||
}
|
}
|
||||||
final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
|
final CharSequence typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull();
|
||||||
if (autoCorrection != null) {
|
|
||||||
final String typedWord = mWordComposer.getTypedWord();
|
final String typedWord = mWordComposer.getTypedWord();
|
||||||
|
final CharSequence autoCorrection = (typedAutoCorrection != null)
|
||||||
|
? typedAutoCorrection : typedWord;
|
||||||
|
if (autoCorrection != null) {
|
||||||
if (TextUtils.isEmpty(typedWord)) {
|
if (TextUtils.isEmpty(typedWord)) {
|
||||||
throw new RuntimeException("We have an auto-correction but the typed word "
|
throw new RuntimeException("We have an auto-correction but the typed word "
|
||||||
+ "is empty? Impossible! I must commit suicide.");
|
+ "is empty? Impossible! I must commit suicide.");
|
||||||
|
@ -1808,7 +1824,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
}
|
}
|
||||||
|
|
||||||
mConnection.beginBatchEdit();
|
mConnection.beginBatchEdit();
|
||||||
if (SPACE_STATE_PHANTOM == mSpaceState && suggestion.length() > 0) {
|
if (SPACE_STATE_PHANTOM == mSpaceState && suggestion.length() > 0
|
||||||
|
// In the batch input mode, a manually picked suggested word should just replace
|
||||||
|
// the current batch input text and there is no need for a phantom space.
|
||||||
|
&& !mWordComposer.isBatchMode()) {
|
||||||
int firstChar = Character.codePointAt(suggestion, 0);
|
int firstChar = Character.codePointAt(suggestion, 0);
|
||||||
if ((!mCurrentSettings.isWeakSpaceStripper(firstChar))
|
if ((!mCurrentSettings.isWeakSpaceStripper(firstChar))
|
||||||
&& (!mCurrentSettings.isWeakSpaceSwapper(firstChar))) {
|
&& (!mCurrentSettings.isWeakSpaceSwapper(firstChar))) {
|
||||||
|
|
|
@ -19,12 +19,16 @@ package com.android.inputmethod.latin;
|
||||||
import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
|
import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.SharedPreferences.Editor;
|
import android.content.SharedPreferences.Editor;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.inputmethodservice.InputMethodService;
|
import android.inputmethodservice.InputMethodService;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.inputmethod.CompletionInfo;
|
import android.view.inputmethod.CompletionInfo;
|
||||||
|
@ -40,6 +44,7 @@ import com.android.inputmethod.latin.RichInputConnection.Range;
|
||||||
import com.android.inputmethod.latin.define.ProductionFlag;
|
import com.android.inputmethod.latin.define.ProductionFlag;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -93,6 +98,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
Character.codePointAt("\uE000", 0); // U+E000 is in the "private-use area"
|
Character.codePointAt("\uE000", 0); // U+E000 is in the "private-use area"
|
||||||
// U+E001 is in the "private-use area"
|
// U+E001 is in the "private-use area"
|
||||||
/* package for test */ static final String WORD_REPLACEMENT_STRING = "\uE001";
|
/* package for test */ static final String WORD_REPLACEMENT_STRING = "\uE001";
|
||||||
|
private static final String PREF_LAST_CLEANUP_TIME = "pref_last_cleanup_time";
|
||||||
|
private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = DateUtils.DAY_IN_MILLIS;
|
||||||
|
private static final long MAX_LOGFILE_AGE_IN_MS = DateUtils.DAY_IN_MILLIS;
|
||||||
// set when LatinIME should ignore an onUpdateSelection() callback that
|
// set when LatinIME should ignore an onUpdateSelection() callback that
|
||||||
// arises from operations in this class
|
// arises from operations in this class
|
||||||
private static boolean sLatinIMEExpectingUpdateSelection = false;
|
private static boolean sLatinIMEExpectingUpdateSelection = false;
|
||||||
|
@ -101,6 +109,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
private Suggest mSuggest;
|
private Suggest mSuggest;
|
||||||
private Dictionary mDictionary;
|
private Dictionary mDictionary;
|
||||||
private KeyboardSwitcher mKeyboardSwitcher;
|
private KeyboardSwitcher mKeyboardSwitcher;
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
private ResearchLogger() {
|
private ResearchLogger() {
|
||||||
}
|
}
|
||||||
|
@ -115,6 +124,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
if (ims == null) {
|
if (ims == null) {
|
||||||
Log.w(TAG, "IMS is null; logging is off");
|
Log.w(TAG, "IMS is null; logging is off");
|
||||||
} else {
|
} else {
|
||||||
|
mContext = ims;
|
||||||
mFilesDir = ims.getFilesDir();
|
mFilesDir = ims.getFilesDir();
|
||||||
if (mFilesDir == null || !mFilesDir.exists()) {
|
if (mFilesDir == null || !mFilesDir.exists()) {
|
||||||
Log.w(TAG, "IME storage directory does not exist.");
|
Log.w(TAG, "IME storage directory does not exist.");
|
||||||
|
@ -124,10 +134,29 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
mUUIDString = getUUID(prefs);
|
mUUIDString = getUUID(prefs);
|
||||||
sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
|
sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
|
||||||
prefs.registerOnSharedPreferenceChangeListener(this);
|
prefs.registerOnSharedPreferenceChangeListener(this);
|
||||||
|
|
||||||
|
final long lastCleanupTime = prefs.getLong(PREF_LAST_CLEANUP_TIME, 0L);
|
||||||
|
final long now = System.currentTimeMillis();
|
||||||
|
if (lastCleanupTime + DURATION_BETWEEN_DIR_CLEANUP_IN_MS < now) {
|
||||||
|
final long timeHorizon = now - MAX_LOGFILE_AGE_IN_MS;
|
||||||
|
cleanupLoggingDir(mFilesDir, timeHorizon);
|
||||||
|
Editor e = prefs.edit();
|
||||||
|
e.putLong(PREF_LAST_CLEANUP_TIME, now);
|
||||||
|
e.apply();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mKeyboardSwitcher = keyboardSwitcher;
|
mKeyboardSwitcher = keyboardSwitcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void cleanupLoggingDir(final File dir, final long time) {
|
||||||
|
for (File file : dir.listFiles()) {
|
||||||
|
if (file.getName().startsWith(ResearchLogger.FILENAME_PREFIX) &&
|
||||||
|
file.lastModified() < time) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private File createLogFile(File filesDir) {
|
private File createLogFile(File filesDir) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
sb.append(FILENAME_PREFIX).append('-');
|
sb.append(FILENAME_PREFIX).append('-');
|
||||||
|
@ -678,18 +707,31 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
|
|
||||||
private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = {
|
private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = {
|
||||||
"LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions",
|
"LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions",
|
||||||
"fieldId", "display", "model", "prefs", "outputFormatVersion"
|
"fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion"
|
||||||
};
|
};
|
||||||
public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
|
public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
|
||||||
final SharedPreferences prefs) {
|
final SharedPreferences prefs) {
|
||||||
|
final ResearchLogger researchLogger = getInstance();
|
||||||
|
researchLogger.start();
|
||||||
if (editorInfo != null) {
|
if (editorInfo != null) {
|
||||||
|
final Context context = researchLogger.mContext;
|
||||||
|
try {
|
||||||
|
final PackageInfo packageInfo;
|
||||||
|
packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),
|
||||||
|
0);
|
||||||
|
final Integer versionCode = packageInfo.versionCode;
|
||||||
|
final String versionName = packageInfo.versionName;
|
||||||
final Object[] values = {
|
final Object[] values = {
|
||||||
getInstance().mUUIDString, editorInfo.packageName,
|
researchLogger.mUUIDString, editorInfo.packageName,
|
||||||
Integer.toHexString(editorInfo.inputType),
|
Integer.toHexString(editorInfo.inputType),
|
||||||
Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId, Build.DISPLAY,
|
Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId,
|
||||||
Build.MODEL, prefs, OUTPUT_FORMAT_VERSION
|
Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName,
|
||||||
|
OUTPUT_FORMAT_VERSION
|
||||||
};
|
};
|
||||||
getInstance().enqueueEvent(EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL, values);
|
researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL, values);
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -300,11 +300,27 @@ public class Suggest {
|
||||||
|
|
||||||
final ArrayList<SuggestedWordInfo> suggestionsContainer =
|
final ArrayList<SuggestedWordInfo> suggestionsContainer =
|
||||||
new ArrayList<SuggestedWordInfo>(suggestionsSet);
|
new ArrayList<SuggestedWordInfo>(suggestionsSet);
|
||||||
|
final int suggestionsCount = suggestionsContainer.size();
|
||||||
|
final boolean isFirstCharCapitalized = wordComposer.isAutoCapitalized();
|
||||||
|
// TODO: Handle the manual temporary shifted mode.
|
||||||
|
// TODO: Should handle TextUtils.CAP_MODE_CHARACTER.
|
||||||
|
final boolean isAllUpperCase = false;
|
||||||
|
if (isFirstCharCapitalized || isAllUpperCase) {
|
||||||
|
for (int i = 0; i < suggestionsCount; ++i) {
|
||||||
|
final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
|
||||||
|
final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
|
||||||
|
wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized,
|
||||||
|
0 /* trailingSingleQuotesCount */);
|
||||||
|
suggestionsContainer.set(i, transformedWordInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SuggestedWordInfo.removeDups(suggestionsContainer);
|
SuggestedWordInfo.removeDups(suggestionsContainer);
|
||||||
|
// In the batch input mode, the most relevant suggested word should act as a "typed word"
|
||||||
|
// (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false).
|
||||||
return new SuggestedWords(suggestionsContainer,
|
return new SuggestedWords(suggestionsContainer,
|
||||||
true /* typedWordValid */,
|
true /* typedWordValid */,
|
||||||
true /* willAutoCorrect */,
|
false /* willAutoCorrect */,
|
||||||
false /* isPunctuationSuggestions */,
|
false /* isPunctuationSuggestions */,
|
||||||
false /* isObsoleteSuggestions */,
|
false /* isObsoleteSuggestions */,
|
||||||
false /* isPrediction */);
|
false /* isPrediction */);
|
||||||
|
|
|
@ -130,9 +130,14 @@ public class WordComposer {
|
||||||
if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) {
|
if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) {
|
||||||
mPrimaryKeyCodes[newIndex] = primaryCode >= Keyboard.CODE_SPACE
|
mPrimaryKeyCodes[newIndex] = primaryCode >= Keyboard.CODE_SPACE
|
||||||
? Character.toLowerCase(primaryCode) : primaryCode;
|
? Character.toLowerCase(primaryCode) : primaryCode;
|
||||||
|
// In the batch input mode, the {@code mInputPointers} holds batch input points and
|
||||||
|
// shouldn't be overridden by the "typed key" coordinates
|
||||||
|
// (See {@link #setBatchInputWord}).
|
||||||
|
if (!mIsBatchMode) {
|
||||||
// TODO: Set correct pointer id and time
|
// TODO: Set correct pointer id and time
|
||||||
mInputPointers.addPointer(newIndex, keyX, keyY, 0, 0);
|
mInputPointers.addPointer(newIndex, keyX, keyY, 0, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
mIsFirstCharCapitalized = isFirstCharCapitalized(
|
mIsFirstCharCapitalized = isFirstCharCapitalized(
|
||||||
newIndex, primaryCode, mIsFirstCharCapitalized);
|
newIndex, primaryCode, mIsFirstCharCapitalized);
|
||||||
if (Character.isUpperCase(primaryCode)) mCapsCount++;
|
if (Character.isUpperCase(primaryCode)) mCapsCount++;
|
||||||
|
@ -144,12 +149,23 @@ public class WordComposer {
|
||||||
mAutoCorrection = null;
|
mAutoCorrection = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We may want to have appendBatchInputPointers() as well.
|
|
||||||
public void setBatchInputPointers(InputPointers batchPointers) {
|
public void setBatchInputPointers(InputPointers batchPointers) {
|
||||||
mInputPointers.copy(batchPointers);
|
mInputPointers.set(batchPointers);
|
||||||
mIsBatchMode = true;
|
mIsBatchMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBatchInputWord(CharSequence word) {
|
||||||
|
reset();
|
||||||
|
mIsBatchMode = true;
|
||||||
|
final int length = word.length();
|
||||||
|
for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) {
|
||||||
|
final int codePoint = Character.codePointAt(word, i);
|
||||||
|
// We don't want to override the batch input points that are held in mInputPointers
|
||||||
|
// (See {@link #add(int,int,int)}).
|
||||||
|
add(codePoint, NOT_A_COORDINATE, NOT_A_COORDINATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal method to retrieve reasonable proximity info for a character.
|
* Internal method to retrieve reasonable proximity info for a character.
|
||||||
*/
|
*/
|
||||||
|
@ -161,7 +177,7 @@ public class WordComposer {
|
||||||
add(codePoint, x, y);
|
add(codePoint, x, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
add(codePoint, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
|
add(codePoint, NOT_A_COORDINATE, NOT_A_COORDINATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -69,15 +69,9 @@
|
||||||
<string name="more_keys_for_c">ç,ć,č</string>
|
<string name="more_keys_for_c">ç,ć,č</string>
|
||||||
<!-- U+00A1: "¡" INVERTED EXCLAMATION MARK
|
<!-- U+00A1: "¡" INVERTED EXCLAMATION MARK
|
||||||
U+00BF: "¿" INVERTED QUESTION MARK -->
|
U+00BF: "¿" INVERTED QUESTION MARK -->
|
||||||
<string name="more_keys_for_punctuation">"!fixedColumnOrder!9,\",\',#,-,¡,!,¿,\\,,\?,\@,&,\\%,+,;,:,/,(,)"</string>
|
<string name="more_keys_for_punctuation">"!fixedColumnOrder!9,¡,\",\',#,-,:,!,\\,,\?,¿,\@,&,\\%,+,;,/,(,)"</string>
|
||||||
<string name="keyhintlabel_for_tablet_comma">¡</string>
|
|
||||||
<string name="more_keys_for_tablet_comma">"¡,!"</string>
|
|
||||||
<string name="keyhintlabel_for_tablet_period">¿</string>
|
|
||||||
<string name="more_keys_for_tablet_period">"¿,\?"</string>
|
|
||||||
<!-- U+00A1: "¡" INVERTED EXCLAMATION MARK -->
|
<!-- U+00A1: "¡" INVERTED EXCLAMATION MARK -->
|
||||||
<string name="keylabel_for_symbols_exclamation">"¡"</string>
|
<string name="more_keys_for_tablet_comma">"!,¡"</string>
|
||||||
<!-- U+00BF: "¿" INVERTED QUESTION MARK -->
|
<!-- U+00BF: "¿" INVERTED QUESTION MARK -->
|
||||||
<string name="keylabel_for_symbols_question">"¿"</string>
|
<string name="more_keys_for_tablet_period">"\?,¿"</string>
|
||||||
<string name="more_keys_for_symbols_exclamation">"!"</string>
|
|
||||||
<string name="more_keys_for_symbols_question">"\?"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -158,7 +158,6 @@
|
||||||
<string name="more_keys_for_symbols_0">ⁿ,∅</string>
|
<string name="more_keys_for_symbols_0">ⁿ,∅</string>
|
||||||
<string name="keylabel_for_comma">,</string>
|
<string name="keylabel_for_comma">,</string>
|
||||||
<string name="more_keys_for_comma"></string>
|
<string name="more_keys_for_comma"></string>
|
||||||
<string name="keylabel_for_symbols_exclamation">!</string>
|
|
||||||
<string name="keylabel_for_symbols_question">\?</string>
|
<string name="keylabel_for_symbols_question">\?</string>
|
||||||
<string name="keylabel_for_symbols_semicolon">;</string>
|
<string name="keylabel_for_symbols_semicolon">;</string>
|
||||||
<string name="keylabel_for_symbols_percent">%</string>
|
<string name="keylabel_for_symbols_percent">%</string>
|
||||||
|
|
Loading…
Reference in New Issue