aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java12
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java21
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java7
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java16
-rw-r--r--java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java26
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java26
-rw-r--r--java/src/com/android/inputmethod/keyboard/ProximityInfo.java5
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java27
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java8
-rw-r--r--java/src/com/android/inputmethod/latin/AutoCorrection.java88
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java68
-rw-r--r--java/src/com/android/inputmethod/latin/BoundedTreeSet.java49
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java18
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java63
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryCollection.java51
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFactory.java11
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java39
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableDictionary.java47
-rw-r--r--java/src/com/android/inputmethod/latin/InputPointers.java131
-rw-r--r--java/src/com/android/inputmethod/latin/LastComposedWord.java14
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java33
-rw-r--r--java/src/com/android/inputmethod/latin/LatinImeLogger.java2
-rw-r--r--java/src/com/android/inputmethod/latin/ResearchLogger.java71
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java16
-rw-r--r--java/src/com/android/inputmethod/latin/Settings.java51
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsValues.java4
-rw-r--r--java/src/com/android/inputmethod/latin/StringUtils.java3
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeLocale.java21
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java424
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java50
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java11
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java10
-rw-r--r--java/src/com/android/inputmethod/latin/UserBinaryDictionary.java14
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryDictionary.java11
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java10
-rw-r--r--java/src/com/android/inputmethod/latin/WhitelistDictionary.java2
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java32
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java19
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java6
39 files changed, 815 insertions, 702 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index 23acb8b74..5ffd94a43 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.accessibility;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseIntArray;
import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.keyboard.Key;
@@ -39,8 +40,8 @@ public class KeyCodeDescriptionMapper {
// Map of key labels to spoken description resource IDs
private final HashMap<CharSequence, Integer> mKeyLabelMap;
- // Map of key codes to spoken description resource IDs
- private final HashMap<Integer, Integer> mKeyCodeMap;
+ // Sparse array of spoken description resource IDs indexed by key codes
+ private final SparseIntArray mKeyCodeMap;
public static void init() {
sInstance.initInternal();
@@ -52,7 +53,7 @@ public class KeyCodeDescriptionMapper {
private KeyCodeDescriptionMapper() {
mKeyLabelMap = new HashMap<CharSequence, Integer>();
- mKeyCodeMap = new HashMap<Integer, Integer>();
+ mKeyCodeMap = new SparseIntArray();
}
private void initInternal() {
@@ -60,7 +61,7 @@ public class KeyCodeDescriptionMapper {
mKeyLabelMap.put(":-)", R.string.spoken_description_smiley);
// Symbols that most TTS engines can't speak
- mKeyCodeMap.put((int) ' ', R.string.spoken_description_space);
+ mKeyCodeMap.put(' ', R.string.spoken_description_space);
// Special non-character codes defined in Keyboard
mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete);
@@ -273,7 +274,8 @@ public class KeyCodeDescriptionMapper {
return context.getString(OBSCURED_KEY_RES_ID);
}
- if (mKeyCodeMap.containsKey(code)) {
+ final int resId = mKeyCodeMap.get(code);
+ if (resId != 0) {
return context.getString(mKeyCodeMap.get(code));
} else if (isDefinedNonCtrl) {
return Character.toString((char) code);
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 6d85fea27..1aec00129 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -23,6 +23,8 @@ import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TypedValue;
import android.util.Xml;
import android.view.InflateException;
@@ -44,7 +46,6 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
@@ -134,7 +135,7 @@ public class Keyboard {
public final Key[] mAltCodeKeysWhileTyping;
public final KeyboardIconsSet mIconsSet;
- private final HashMap<Integer, Key> mKeyCache = new HashMap<Integer, Key>();
+ private final SparseArray<Key> mKeyCache = new SparseArray<Key>();
private final ProximityInfo mProximityInfo;
private final boolean mProximityCharsCorrectionEnabled;
@@ -202,7 +203,7 @@ public class Keyboard {
}
public boolean hasKey(Key aKey) {
- if (mKeyCache.containsKey(aKey)) {
+ if (mKeyCache.indexOfValue(aKey) >= 0) {
return true;
}
@@ -346,8 +347,8 @@ public class Keyboard {
private int mMaxHeightCount = 0;
private int mMaxWidthCount = 0;
- private final HashMap<Integer, Integer> mHeightHistogram = new HashMap<Integer, Integer>();
- private final HashMap<Integer, Integer> mWidthHistogram = new HashMap<Integer, Integer>();
+ private final SparseIntArray mHeightHistogram = new SparseIntArray();
+ private final SparseIntArray mWidthHistogram = new SparseIntArray();
private void clearHistogram() {
mMostCommonKeyHeight = 0;
@@ -359,22 +360,22 @@ public class Keyboard {
mWidthHistogram.clear();
}
- private static int updateHistogramCounter(HashMap<Integer, Integer> histogram,
- Integer key) {
- final int count = (histogram.containsKey(key) ? histogram.get(key) : 0) + 1;
+ private static int updateHistogramCounter(SparseIntArray histogram, int key) {
+ final int index = histogram.indexOfKey(key);
+ final int count = (index >= 0 ? histogram.get(key) : 0) + 1;
histogram.put(key, count);
return count;
}
private void updateHistogram(Key key) {
- final Integer height = key.mHeight + key.mVerticalGap;
+ final int height = key.mHeight + key.mVerticalGap;
final int heightCount = updateHistogramCounter(mHeightHistogram, height);
if (heightCount > mMaxHeightCount) {
mMaxHeightCount = heightCount;
mMostCommonKeyHeight = height;
}
- final Integer width = key.mWidth + key.mHorizontalGap;
+ final int width = key.mWidth + key.mHorizontalGap;
final int widthCount = updateHistogramCounter(mWidthHistogram, width);
if (widthCount > mMaxWidthCount) {
mMaxWidthCount = widthCount;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index 8c7246855..aab89a3e5 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -29,6 +29,7 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.text.InputType;
import android.util.Log;
+import android.util.SparseArray;
import android.util.Xml;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -116,9 +117,9 @@ public class KeyboardLayoutSet {
InputMethodSubtype mSubtype;
int mOrientation;
int mWidth;
- // KeyboardLayoutSet element id to element's parameters map.
- final HashMap<Integer, ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
- new HashMap<Integer, ElementParams>();
+ // Sparse array of KeyboardLayoutSet element parameters indexed by element's id.
+ final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
+ new SparseArray<ElementParams>();
static class ElementParams {
int mKeyboardXmlId;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 18e01fb49..fb98af3e6 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -30,6 +30,7 @@ import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Message;
import android.util.AttributeSet;
+import android.util.SparseArray;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
@@ -42,7 +43,6 @@ import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
import com.android.inputmethod.latin.StringUtils;
-import java.util.HashMap;
import java.util.HashSet;
/**
@@ -124,12 +124,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
private Canvas mCanvas;
private final Paint mPaint = new Paint();
private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics();
- // This map caches key label text height in pixel as value and key label text size as map key.
- private static final HashMap<Integer, Float> sTextHeightCache =
- new HashMap<Integer, Float>();
- // This map caches key label text width in pixel as value and key label text size as map key.
- private static final HashMap<Integer, Float> sTextWidthCache =
- new HashMap<Integer, Float>();
+ // This sparse array caches key label text height in pixel indexed by key label text size.
+ private static final SparseArray<Float> sTextHeightCache = new SparseArray<Float>();
+ // This sparse array caches key label text width in pixel indexed by key label text size.
+ private static final SparseArray<Float> sTextWidthCache = new SparseArray<Float>();
private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' };
private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' };
@@ -766,7 +764,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
private final Rect mTextBounds = new Rect();
private float getCharHeight(char[] referenceChar, Paint paint) {
- final Integer key = getCharGeometryCacheKey(referenceChar[0], paint);
+ final int key = getCharGeometryCacheKey(referenceChar[0], paint);
final Float cachedValue = sTextHeightCache.get(key);
if (cachedValue != null)
return cachedValue;
@@ -778,7 +776,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
private float getCharWidth(char[] referenceChar, Paint paint) {
- final Integer key = getCharGeometryCacheKey(referenceChar[0], paint);
+ final int key = getCharGeometryCacheKey(referenceChar[0], paint);
final Float cachedValue = sTextWidthCache.get(key);
if (cachedValue != null)
return cachedValue;
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index 383298de9..7714ba892 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -127,7 +127,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
private static final int MSG_TYPING_STATE_EXPIRED = 4;
private final KeyTimerParams mParams;
- private boolean mInKeyRepeat;
public KeyTimerHandler(LatinKeyboardView outerInstance, KeyTimerParams params) {
super(outerInstance);
@@ -140,8 +139,11 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
final PointerTracker tracker = (PointerTracker) msg.obj;
switch (msg.what) {
case MSG_REPEAT_KEY:
- tracker.onRegisterKey(tracker.getKey());
- startKeyRepeatTimer(tracker, mParams.mKeyRepeatInterval);
+ final Key currentKey = tracker.getKey();
+ if (currentKey != null && currentKey.mCode == msg.arg1) {
+ tracker.onRegisterKey(currentKey);
+ startKeyRepeatTimer(tracker, mParams.mKeyRepeatInterval);
+ }
break;
case MSG_LONGPRESS_KEY:
if (tracker != null) {
@@ -158,22 +160,23 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
private void startKeyRepeatTimer(PointerTracker tracker, long delay) {
- sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, tracker), delay);
+ final Key key = tracker.getKey();
+ if (key == null) return;
+ sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
}
@Override
public void startKeyRepeatTimer(PointerTracker tracker) {
- mInKeyRepeat = true;
startKeyRepeatTimer(tracker, mParams.mKeyRepeatStartTimeout);
}
public void cancelKeyRepeatTimer() {
- mInKeyRepeat = false;
removeMessages(MSG_REPEAT_KEY);
}
+ // TODO: Suppress layout changes in key repeat mode
public boolean isInKeyRepeat() {
- return mInKeyRepeat;
+ return hasMessages(MSG_REPEAT_KEY);
}
@Override
@@ -451,8 +454,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
*/
@Override
public void setKeyboard(Keyboard keyboard) {
- // Remove any pending messages, except dismissing preview
- mKeyTimerHandler.cancelKeyTimers();
+ // Remove any pending messages, except dismissing preview and key repeat.
+ mKeyTimerHandler.cancelLongPressTimer();
super.setKeyboard(keyboard);
mKeyDetector.setKeyboard(
keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
@@ -755,15 +758,18 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
final PointerTracker tracker = PointerTracker.getPointerTracker(
pointerId, this);
final int px, py;
+ final MotionEvent motionEvent;
if (mMoreKeysPanel != null
&& tracker.mPointerId == mMoreKeysPanelPointerTrackerId) {
px = mMoreKeysPanel.translateX((int)me.getX(i));
py = mMoreKeysPanel.translateY((int)me.getY(i));
+ motionEvent = null;
} else {
px = (int)me.getX(i);
py = (int)me.getY(i);
+ motionEvent = me;
}
- tracker.onMoveEvent(px, py, eventTime);
+ tracker.onMoveEvent(px, py, eventTime, motionEvent);
if (ENABLE_USABILITY_STUDY_LOG) {
final float pointerSize = me.getSize(i);
final float pointerPressure = me.getPressure(i);
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 34e428e82..32ef408b4 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -148,9 +148,6 @@ public class PointerTracker {
// true if this pointer has been long-pressed and is showing a more keys panel.
private boolean mIsShowingMoreKeysPanel;
- // true if this pointer is repeatable key
- private boolean mIsRepeatableKey;
-
// true if this pointer is in sliding key input
boolean mIsInSlidingKeyInput;
@@ -319,6 +316,13 @@ public class PointerTracker {
private void setKeyDetectorInner(KeyDetector keyDetector) {
mKeyDetector = keyDetector;
mKeyboard = keyDetector.getKeyboard();
+ final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
+ if (newKey != mCurrentKey) {
+ if (mDrawingProxy != null) {
+ setReleasedKeyGraphics(mCurrentKey);
+ }
+ mCurrentKey = newKey;
+ }
final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4;
mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
}
@@ -465,7 +469,7 @@ public class PointerTracker {
onUpEvent(x, y, eventTime);
break;
case MotionEvent.ACTION_MOVE:
- onMoveEvent(x, y, eventTime);
+ onMoveEvent(x, y, eventTime, null);
break;
case MotionEvent.ACTION_CANCEL:
onCancelEvent(x, y, eventTime);
@@ -521,7 +525,6 @@ public class PointerTracker {
|| mKeyDetector.alwaysAllowsSlidingInput();
mKeyboardLayoutHasBeenChanged = false;
mKeyAlreadyProcessed = false;
- mIsRepeatableKey = false;
mIsInSlidingKeyInput = false;
mIgnoreModifierKey = false;
if (key != null) {
@@ -545,7 +548,7 @@ public class PointerTracker {
mIsInSlidingKeyInput = true;
}
- public void onMoveEvent(int x, int y, long eventTime) {
+ public void onMoveEvent(int x, int y, long eventTime, MotionEvent me) {
if (DEBUG_MOVE_EVENT)
printTouchEvent("onMoveEvent:", x, y, eventTime);
if (mKeyAlreadyProcessed)
@@ -668,7 +671,7 @@ public class PointerTracker {
}
if (mKeyAlreadyProcessed)
return;
- if (!mIsRepeatableKey) {
+ if (mCurrentKey != null && !mCurrentKey.isRepeatable()) {
detectAndSendKey(mCurrentKey, mKeyX, mKeyY);
}
}
@@ -714,9 +717,6 @@ public class PointerTracker {
if (key != null && key.isRepeatable()) {
onRegisterKey(key);
mTimerProxy.startKeyRepeatTimer(this);
- mIsRepeatableKey = true;
- } else {
- mIsRepeatableKey = false;
}
}
@@ -760,14 +760,10 @@ public class PointerTracker {
callListenerOnRelease(key, code, false);
}
- private long mPreviousEventTime;
-
private void printTouchEvent(String title, int x, int y, long eventTime) {
final Key key = mKeyDetector.detectHitKey(x, y);
final String code = KeyDetector.printableCode(key);
- final long delta = eventTime - mPreviousEventTime;
Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %s", title,
- (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, delta, code));
- mPreviousEventTime = eventTime;
+ (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, eventTime, code));
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index 1bc825479..ae123e29a 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -24,7 +24,6 @@ import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection;
import com.android.inputmethod.latin.JniUtils;
import java.util.Arrays;
-import java.util.HashMap;
public class ProximityInfo {
/** MAX_PROXIMITY_CHARS_SIZE must be the same as MAX_PROXIMITY_CHARS_SIZE_INTERNAL
@@ -190,10 +189,6 @@ public class ProximityInfo {
private void computeNearestNeighbors() {
final int defaultWidth = mMostCommonKeyWidth;
final Key[] keys = mKeys;
- final HashMap<Integer, Key> keyCodeMap = new HashMap<Integer, Key>();
- for (final Key key : keys) {
- keyCodeMap.put(key.mCode, key);
- }
final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE);
final int threshold = thresholdBase * thresholdBase;
// Round-up so we don't have any pixels outside the grid
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
index 80f4f259b..291b3b943 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard.internal;
import android.content.res.TypedArray;
import android.util.Log;
+import android.util.SparseArray;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.R;
@@ -89,7 +90,7 @@ public class KeyStyles {
private class DeclaredKeyStyle extends KeyStyle {
private final String mParentStyleName;
- private final HashMap<Integer, Object> mStyleAttributes = new HashMap<Integer, Object>();
+ private final SparseArray<Object> mStyleAttributes = new SparseArray<Object>();
public DeclaredKeyStyle(String parentStyleName) {
mParentStyleName = parentStyleName;
@@ -100,8 +101,9 @@ public class KeyStyles {
if (a.hasValue(index)) {
return parseStringArray(a, index);
}
- if (mStyleAttributes.containsKey(index)) {
- return (String[])mStyleAttributes.get(index);
+ final Object value = mStyleAttributes.get(index);
+ if (value != null) {
+ return (String[])value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getStringArray(a, index);
@@ -112,8 +114,9 @@ public class KeyStyles {
if (a.hasValue(index)) {
return parseString(a, index);
}
- if (mStyleAttributes.containsKey(index)) {
- return (String)mStyleAttributes.get(index);
+ final Object value = mStyleAttributes.get(index);
+ if (value != null) {
+ return (String)value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getString(a, index);
@@ -124,8 +127,9 @@ public class KeyStyles {
if (a.hasValue(index)) {
return a.getInt(index, defaultValue);
}
- if (mStyleAttributes.containsKey(index)) {
- return (Integer)mStyleAttributes.get(index);
+ final Object value = mStyleAttributes.get(index);
+ if (value != null) {
+ return (Integer)value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getInt(a, index, defaultValue);
@@ -133,12 +137,13 @@ public class KeyStyles {
@Override
public int getFlag(TypedArray a, int index) {
- int value = a.getInt(index, 0);
- if (mStyleAttributes.containsKey(index)) {
- value |= (Integer)mStyleAttributes.get(index);
+ int flags = a.getInt(index, 0);
+ final Object value = mStyleAttributes.get(index);
+ if (value != null) {
+ flags |= (Integer)value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
- return value | parentStyle.getFlag(a, index);
+ return flags | parentStyle.getFlag(a, index);
}
void readKeyAttributes(TypedArray keyAttr) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
index 540e63b3f..5155851fe 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
@@ -20,6 +20,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.Log;
+import android.util.SparseIntArray;
import com.android.inputmethod.latin.R;
@@ -31,8 +32,7 @@ public class KeyboardIconsSet {
public static final int ICON_UNDEFINED = 0;
private static final int ATTR_UNDEFINED = 0;
- private static final HashMap<Integer, Integer> ATTR_ID_TO_ICON_ID
- = new HashMap<Integer, Integer>();
+ private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray();
// Icon name to icon id map.
private static final HashMap<String, Integer> sNameToIdsMap = new HashMap<String, Integer>();
@@ -76,7 +76,9 @@ public class KeyboardIconsSet {
}
public void loadIcons(final TypedArray keyboardAttrs) {
- for (final Integer attrId : ATTR_ID_TO_ICON_ID.keySet()) {
+ final int size = ATTR_ID_TO_ICON_ID.size();
+ for (int index = 0; index < size; index++) {
+ final int attrId = ATTR_ID_TO_ICON_ID.keyAt(index);
try {
final Drawable icon = keyboardAttrs.getDrawable(attrId);
setDefaultBounds(icon);
diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java
index e0452483c..c78974dac 100644
--- a/java/src/com/android/inputmethod/latin/AutoCorrection.java
+++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java
@@ -21,34 +21,17 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import android.text.TextUtils;
import android.util.Log;
-import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
public class AutoCorrection {
private static final boolean DBG = LatinImeLogger.sDBG;
private static final String TAG = AutoCorrection.class.getSimpleName();
+ private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4;
private AutoCorrection() {
// Purely static class: can't instantiate.
}
- public static CharSequence computeAutoCorrectionWord(
- final ConcurrentHashMap<String, Dictionary> dictionaries,
- final WordComposer wordComposer, final ArrayList<SuggestedWordInfo> suggestions,
- final CharSequence consideredWord, final float autoCorrectionThreshold,
- final CharSequence whitelistedWord) {
- if (hasAutoCorrectionForWhitelistedWord(whitelistedWord)) {
- return whitelistedWord;
- } else if (hasAutoCorrectionForConsideredWord(
- dictionaries, wordComposer, suggestions, consideredWord)) {
- return consideredWord;
- } else if (hasAutoCorrectionForBinaryDictionary(wordComposer, suggestions,
- consideredWord, autoCorrectionThreshold)) {
- return suggestions.get(0).mWord;
- }
- return null;
- }
-
public static boolean isValidWord(final ConcurrentHashMap<String, Dictionary> dictionaries,
CharSequence word, boolean ignoreCase) {
if (TextUtils.isEmpty(word)) {
@@ -56,7 +39,7 @@ public class AutoCorrection {
}
final CharSequence lowerCasedWord = word.toString().toLowerCase();
for (final String key : dictionaries.keySet()) {
- if (key.equals(Suggest.DICT_KEY_WHITELIST)) continue;
+ if (key.equals(Dictionary.TYPE_WHITELIST)) continue;
final Dictionary dictionary = dictionaries.get(key);
// It's unclear how realistically 'dictionary' can be null, but the monkey is somehow
// managing to get null in here. Presumably the language is changing to a language with
@@ -81,7 +64,7 @@ public class AutoCorrection {
}
int maxFreq = -1;
for (final String key : dictionaries.keySet()) {
- if (key.equals(Suggest.DICT_KEY_WHITELIST)) continue;
+ if (key.equals(Dictionary.TYPE_WHITELIST)) continue;
final Dictionary dictionary = dictionaries.get(key);
if (null == dictionary) continue;
final int tempFreq = dictionary.getFrequency(word);
@@ -92,11 +75,12 @@ public class AutoCorrection {
return maxFreq;
}
- public static boolean allowsToBeAutoCorrected(
+ // Returns true if this is a whitelist entry, or it isn't in any dictionary.
+ public static boolean isWhitelistedOrNotAWord(
final ConcurrentHashMap<String, Dictionary> dictionaries,
final CharSequence word, final boolean ignoreCase) {
final WhitelistDictionary whitelistDictionary =
- (WhitelistDictionary)dictionaries.get(Suggest.DICT_KEY_WHITELIST);
+ (WhitelistDictionary)dictionaries.get(Dictionary.TYPE_WHITELIST);
// If "word" is in the whitelist dictionary, it should not be auto corrected.
if (whitelistDictionary != null
&& whitelistDictionary.shouldForciblyAutoCorrectFrom(word)) {
@@ -105,33 +89,18 @@ public class AutoCorrection {
return !isValidWord(dictionaries, word, ignoreCase);
}
- private static boolean hasAutoCorrectionForWhitelistedWord(CharSequence whiteListedWord) {
- return whiteListedWord != null;
- }
-
- private static boolean hasAutoCorrectionForConsideredWord(
- final ConcurrentHashMap<String, Dictionary> dictionaries,
- final WordComposer wordComposer, final ArrayList<SuggestedWordInfo> suggestions,
- final CharSequence consideredWord) {
- if (TextUtils.isEmpty(consideredWord)) return false;
- return wordComposer.size() > 1 && suggestions.size() > 0
- && !allowsToBeAutoCorrected(dictionaries, consideredWord, false);
- }
-
- private static boolean hasAutoCorrectionForBinaryDictionary(WordComposer wordComposer,
- ArrayList<SuggestedWordInfo> suggestions,
+ public static boolean suggestionExceedsAutoCorrectionThreshold(SuggestedWordInfo suggestion,
CharSequence consideredWord, float autoCorrectionThreshold) {
- if (wordComposer.size() > 1 && suggestions.size() > 0) {
- final SuggestedWordInfo autoCorrectionSuggestion = suggestions.get(0);
+ if (null != suggestion) {
//final int autoCorrectionSuggestionScore = sortedScores[0];
- final int autoCorrectionSuggestionScore = autoCorrectionSuggestion.mScore;
+ final int autoCorrectionSuggestionScore = suggestion.mScore;
// TODO: when the normalized score of the first suggestion is nearly equals to
// the normalized score of the second suggestion, behave less aggressive.
final float normalizedScore = BinaryDictionary.calcNormalizedScore(
- consideredWord.toString(), autoCorrectionSuggestion.mWord.toString(),
+ consideredWord.toString(), suggestion.mWord.toString(),
autoCorrectionSuggestionScore);
if (DBG) {
- Log.d(TAG, "Normalized " + consideredWord + "," + autoCorrectionSuggestion + ","
+ Log.d(TAG, "Normalized " + consideredWord + "," + suggestion + ","
+ autoCorrectionSuggestionScore + ", " + normalizedScore
+ "(" + autoCorrectionThreshold + ")");
}
@@ -139,10 +108,43 @@ public class AutoCorrection {
if (DBG) {
Log.d(TAG, "Auto corrected by S-threshold.");
}
- return true;
+ return !shouldBlockAutoCorrectionBySafetyNet(consideredWord.toString(),
+ suggestion.mWord);
}
}
return false;
}
+ // TODO: Resolve the inconsistencies between the native auto correction algorithms and
+ // this safety net
+ public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord,
+ final CharSequence suggestion) {
+ // Safety net for auto correction.
+ // Actually if we hit this safety net, it's a bug.
+ // If user selected aggressive auto correction mode, there is no need to use the safety
+ // net.
+ // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH,
+ // we should not use net because relatively edit distance can be big.
+ final int typedWordLength = typedWord.length();
+ if (typedWordLength < MINIMUM_SAFETY_NET_CHAR_LENGTH) {
+ return false;
+ }
+ final int maxEditDistanceOfNativeDictionary =
+ (typedWordLength < 5 ? 2 : typedWordLength / 2) + 1;
+ final int distance = BinaryDictionary.editDistance(typedWord, suggestion.toString());
+ if (DBG) {
+ Log.d(TAG, "Autocorrected edit distance = " + distance
+ + ", " + maxEditDistanceOfNativeDictionary);
+ }
+ if (distance > maxEditDistanceOfNativeDictionary) {
+ if (DBG) {
+ Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestion);
+ Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. "
+ + "Turning off auto-correction.");
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index d0613bd72..ae415d0ab 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -20,7 +20,9 @@ import android.content.Context;
import android.text.TextUtils;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
@@ -40,17 +42,18 @@ public class BinaryDictionary extends Dictionary {
*/
public static final int MAX_WORD_LENGTH = 48;
public static final int MAX_WORDS = 18;
+ public static final int MAX_SPACES = 16;
private static final String TAG = "BinaryDictionary";
private static final int MAX_BIGRAMS = 60;
private static final int TYPED_LETTER_MULTIPLIER = 2;
- private int mDicTypeId;
private long mNativeDict;
private final int[] mInputCodes = new int[MAX_WORD_LENGTH];
private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS];
private final char[] mOutputChars_bigrams = new char[MAX_WORD_LENGTH * MAX_BIGRAMS];
+ private final int[] mSpaceIndices = new int[MAX_SPACES];
private final int[] mScores = new int[MAX_WORDS];
private final int[] mBigramScores = new int[MAX_BIGRAMS];
@@ -65,14 +68,12 @@ public class BinaryDictionary extends Dictionary {
* @param offset the offset of the dictionary data within the file.
* @param length the length of the binary data.
* @param useFullEditDistance whether to use the full edit distance in suggestions
+ * @param dictType the dictionary type, as a human-readable string
*/
public BinaryDictionary(final Context context,
final String filename, final long offset, final long length,
- final boolean useFullEditDistance, final Locale locale) {
- // Note: at the moment a binary dictionary is always of the "main" type.
- // Initializing this here will help transitioning out of the scheme where
- // the Suggest class knows everything about every single dictionary.
- mDicTypeId = Suggest.DIC_MAIN;
+ final boolean useFullEditDistance, final Locale locale, final String dictType) {
+ super(dictType);
mUseFullEditDistance = useFullEditDistance;
loadDictionary(filename, offset, length);
}
@@ -87,8 +88,10 @@ public class BinaryDictionary extends Dictionary {
private native int getFrequencyNative(long dict, int[] word, int wordLength);
private native boolean isValidBigramNative(long dict, int[] word1, int[] word2);
private native int getSuggestionsNative(long dict, long proximityInfo, int[] xCoordinates,
- int[] yCoordinates, int[] inputCodes, int codesSize, int[] prevWordForBigrams,
- boolean useFullEditDistance, char[] outputChars, int[] scores);
+ int[] yCoordinates, int[] times, int[] pointerIds, int[] inputCodes, int codesSize,
+ int commitPoint, boolean isGesture,
+ int[] prevWordCodePointArray, boolean useFullEditDistance, char[] outputChars,
+ int[] scores, int[] outputIndices);
private native int getBigramsNative(long dict, int[] prevWord, int prevWordLength,
int[] inputCodes, int inputCodesLength, char[] outputChars, int[] scores,
int maxWordLength, int maxBigrams);
@@ -103,9 +106,9 @@ public class BinaryDictionary extends Dictionary {
}
@Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
- if (mNativeDict == 0) return;
+ public ArrayList<SuggestedWordInfo> getBigrams(final WordComposer codes,
+ final CharSequence previousWord) {
+ if (mNativeDict == 0) return null;
int[] codePoints = StringUtils.toCodePointArray(previousWord.toString());
Arrays.fill(mOutputChars_bigrams, (char) 0);
@@ -123,6 +126,7 @@ public class BinaryDictionary extends Dictionary {
count = MAX_BIGRAMS;
}
+ final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>();
for (int j = 0; j < count; ++j) {
if (codesSize > 0 && mBigramScores[j] < 1) break;
final int start = j * MAX_WORD_LENGTH;
@@ -131,19 +135,22 @@ public class BinaryDictionary extends Dictionary {
++len;
}
if (len > 0) {
- callback.addWord(mOutputChars_bigrams, start, len, mBigramScores[j],
- mDicTypeId, Dictionary.BIGRAM);
+ suggestions.add(new SuggestedWordInfo(
+ new String(mOutputChars_bigrams, start, len),
+ mBigramScores[j], SuggestedWordInfo.KIND_CORRECTION, mDictType));
}
}
+ return suggestions;
}
// proximityInfo and/or prevWordForBigrams may not be null.
@Override
- public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams,
- final WordCallback callback, final ProximityInfo proximityInfo) {
+ public ArrayList<SuggestedWordInfo> getWords(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
final int count = getSuggestions(codes, prevWordForBigrams, proximityInfo, mOutputChars,
- mScores);
+ mScores, mSpaceIndices);
+ final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>();
for (int j = 0; j < count; ++j) {
if (mScores[j] < 1) break;
final int start = j * MAX_WORD_LENGTH;
@@ -152,10 +159,13 @@ public class BinaryDictionary extends Dictionary {
++len;
}
if (len > 0) {
- callback.addWord(mOutputChars, start, len, mScores[j], mDicTypeId,
- Dictionary.UNIGRAM);
+ // TODO: actually get the kind from native code
+ suggestions.add(new SuggestedWordInfo(
+ new String(mOutputChars, start, len),
+ mScores[j], SuggestedWordInfo.KIND_CORRECTION, mDictType));
}
}
+ return suggestions;
}
/* package for test */ boolean isValidDictionary() {
@@ -165,7 +175,7 @@ public class BinaryDictionary extends Dictionary {
// proximityInfo may not be null.
/* package for test */ int getSuggestions(final WordComposer codes,
final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo,
- char[] outputChars, int[] scores) {
+ char[] outputChars, int[] scores, int[] spaceIndices) {
if (!isValidDictionary()) return -1;
final int codesSize = codes.size();
@@ -179,14 +189,22 @@ public class BinaryDictionary extends Dictionary {
Arrays.fill(outputChars, (char) 0);
Arrays.fill(scores, 0);
- final int[] prevWordCodePointArray = null == prevWordForBigrams
+ // TODO: toLowerCase in the native code
+ final int[] prevWordCodePointArray = (null == prevWordForBigrams)
? null : StringUtils.toCodePointArray(prevWordForBigrams.toString());
- // TODO: pass the previous word to native code
- return getSuggestionsNative(
- mNativeDict, proximityInfo.getNativeProximityInfo(),
- codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize,
- prevWordCodePointArray, mUseFullEditDistance, outputChars, scores);
+ int[] emptyArray = new int[codesSize];
+ Arrays.fill(emptyArray, 0);
+
+ //final int commitPoint = codes.getCommitPoint();
+ //codes.clearCommitPoint();
+
+ final InputPointers ips = codes.getInputPointers();
+
+ return getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(),
+ ips.getXCoordinates(), ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(),
+ mInputCodes, codesSize, 0 /* unused */, false, prevWordCodePointArray,
+ mUseFullEditDistance, outputChars, scores, spaceIndices);
}
public static float calcNormalizedScore(String before, String after, int score) {
diff --git a/java/src/com/android/inputmethod/latin/BoundedTreeSet.java b/java/src/com/android/inputmethod/latin/BoundedTreeSet.java
new file mode 100644
index 000000000..cf977617d
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/BoundedTreeSet.java
@@ -0,0 +1,49 @@
+/*
+ * 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.latin;
+
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.TreeSet;
+
+/**
+ * A TreeSet that is bounded in size and throws everything that's smaller than its limit
+ */
+public class BoundedTreeSet extends TreeSet<SuggestedWordInfo> {
+ private final int mCapacity;
+ public BoundedTreeSet(final Comparator<SuggestedWordInfo> comparator, final int capacity) {
+ super(comparator);
+ mCapacity = capacity;
+ }
+
+ @Override
+ public boolean add(final SuggestedWordInfo e) {
+ if (size() < mCapacity) return super.add(e);
+ if (comparator().compare(e, last()) > 0) return false;
+ super.add(e);
+ pollLast(); // removes the last element
+ return true;
+ }
+
+ @Override
+ public boolean addAll(final Collection<? extends SuggestedWordInfo> e) {
+ if (null == e) return false;
+ return super.addAll(e);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index 10e511eaf..5edc4314f 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -62,8 +62,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
*/
private final boolean mUseFirstLastBigrams;
- public ContactsBinaryDictionary(final Context context, final int dicTypeId, Locale locale) {
- super(context, getFilenameWithLocale(NAME, locale.toString()), dicTypeId);
+ public ContactsBinaryDictionary(final Context context, Locale locale) {
+ super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS);
mLocale = locale;
mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
registerObserver(context);
@@ -120,12 +120,6 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
}
}
- @Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
- super.getBigrams(codes, previousWord, callback);
- }
-
private boolean useFirstLastBigramsForLocale(Locale locale) {
// TODO: Add firstname/lastname bigram rules for other languages.
if (locale != null && locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
@@ -167,7 +161,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
* bigrams depending on locale.
*/
private void addName(String name) {
- int len = name.codePointCount(0, name.length());
+ int len = StringUtils.codePointCount(name);
String prevWord = null;
// TODO: Better tokenization for non-Latin writing systems
for (int i = 0; i < len; i++) {
@@ -177,7 +171,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
i = end - 1;
// Don't add single letter words, possibly confuses
// capitalization of i.
- final int wordLen = word.codePointCount(0, word.length());
+ final int wordLen = StringUtils.codePointCount(word);
if (wordLen < MAX_WORD_LENGTH && wordLen > 1) {
super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS);
if (!TextUtils.isEmpty(prevWord)) {
@@ -266,14 +260,14 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
* Checks if the words in a name are in the current binary dictionary.
*/
private boolean isNameInDictionary(String name) {
- int len = name.codePointCount(0, name.length());
+ int len = StringUtils.codePointCount(name);
String prevWord = null;
for (int i = 0; i < len; i++) {
if (Character.isLetter(name.codePointAt(i))) {
int end = getWordEndPosition(name, len, i);
String word = name.substring(i, end);
i = end - 1;
- final int wordLen = word.codePointCount(0, word.length());
+ final int wordLen = StringUtils.codePointCount(word);
if (wordLen < MAX_WORD_LENGTH && wordLen > 1) {
if (!TextUtils.isEmpty(prevWord) && mUseFirstLastBigrams) {
if (!super.isValidBigramLocked(prevWord, word)) {
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 9c3d46e70..0835450c1 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -17,6 +17,9 @@
package com.android.inputmethod.latin;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
+import java.util.ArrayList;
/**
* Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key
@@ -28,55 +31,43 @@ public abstract class Dictionary {
*/
protected static final int FULL_WORD_SCORE_MULTIPLIER = 2;
- public static final int UNIGRAM = 0;
- public static final int BIGRAM = 1;
-
public static final int NOT_A_PROBABILITY = -1;
- /**
- * Interface to be implemented by classes requesting words to be fetched from the dictionary.
- * @see #getWords(WordComposer, CharSequence, WordCallback, ProximityInfo)
- */
- public interface WordCallback {
- /**
- * Adds a word to a list of suggestions. The word is expected to be ordered based on
- * the provided score.
- * @param word the character array containing the word
- * @param wordOffset starting offset of the word in the character array
- * @param wordLength length of valid characters in the character array
- * @param score the score of occurrence. This is normalized between 1 and 255, but
- * can exceed those limits
- * @param dicTypeId of the dictionary where word was from
- * @param dataType tells type of this data, either UNIGRAM or BIGRAM
- * @return true if the word was added, false if no more words are required
- */
- boolean addWord(char[] word, int wordOffset, int wordLength, int score, int dicTypeId,
- int dataType);
+
+ public static final String TYPE_USER_TYPED = "user_typed";
+ public static final String TYPE_APPLICATION_DEFINED = "application_defined";
+ public static final String TYPE_HARDCODED = "hardcoded"; // punctuation signs and such
+ public static final String TYPE_MAIN = "main";
+ public static final String TYPE_CONTACTS = "contacts";
+ // User dictionary, the system-managed one.
+ public static final String TYPE_USER = "user";
+ // User history dictionary internal to LatinIME.
+ public static final String TYPE_USER_HISTORY = "history";
+ public static final String TYPE_WHITELIST ="whitelist";
+ protected final String mDictType;
+
+ public Dictionary(final String dictType) {
+ mDictType = dictType;
}
/**
* Searches for words in the dictionary that match the characters in the composer. Matched
- * words are added through the callback object.
- * @param composer the key sequence to match
+ * words are returned as an ArrayList.
+ * @param composer the key sequence to match with coordinate info, as a WordComposer
* @param prevWordForBigrams the previous word, or null if none
- * @param callback the callback object to send matched words to as possible candidates
* @param proximityInfo the object for key proximity. May be ignored by some implementations.
- * @see WordCallback#addWord(char[], int, int, int, int, int)
+ * @return the list of suggestions
*/
- abstract public void getWords(final WordComposer composer,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo);
+ abstract public ArrayList<SuggestedWordInfo> getWords(final WordComposer composer,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo);
/**
- * Searches for pairs in the bigram dictionary that matches the previous word and all the
- * possible words following are added through the callback object.
+ * Searches for pairs in the bigram dictionary that matches the previous word.
* @param composer the key sequence to match
* @param previousWord the word before
- * @param callback the callback object to send possible word following previous word
+ * @return the list of suggestions
*/
- public void getBigrams(final WordComposer composer, final CharSequence previousWord,
- final WordCallback callback) {
- // empty base implementation
- }
+ public abstract ArrayList<SuggestedWordInfo> getBigrams(final WordComposer composer,
+ final CharSequence previousWord);
/**
* Checks if the given word occurs in the dictionary
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index 26c2e637e..dcc53c59f 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -17,9 +17,11 @@
package com.android.inputmethod.latin;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import android.util.Log;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -31,11 +33,13 @@ public class DictionaryCollection extends Dictionary {
private final String TAG = DictionaryCollection.class.getSimpleName();
protected final CopyOnWriteArrayList<Dictionary> mDictionaries;
- public DictionaryCollection() {
+ public DictionaryCollection(final String dictType) {
+ super(dictType);
mDictionaries = new CopyOnWriteArrayList<Dictionary>();
}
- public DictionaryCollection(Dictionary... dictionaries) {
+ public DictionaryCollection(final String dictType, Dictionary... dictionaries) {
+ super(dictType);
if (null == dictionaries) {
mDictionaries = new CopyOnWriteArrayList<Dictionary>();
} else {
@@ -44,23 +48,48 @@ public class DictionaryCollection extends Dictionary {
}
}
- public DictionaryCollection(Collection<Dictionary> dictionaries) {
+ public DictionaryCollection(final String dictType, Collection<Dictionary> dictionaries) {
+ super(dictType);
mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
mDictionaries.removeAll(Collections.singleton(null));
}
@Override
- public void getWords(final WordComposer composer, final CharSequence prevWordForBigrams,
- final WordCallback callback, final ProximityInfo proximityInfo) {
- for (final Dictionary dict : mDictionaries)
- dict.getWords(composer, prevWordForBigrams, callback, proximityInfo);
+ public ArrayList<SuggestedWordInfo> getWords(final WordComposer composer,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries;
+ if (dictionaries.isEmpty()) return null;
+ // To avoid creating unnecessary objects, we get the list out of the first
+ // dictionary and add the rest to it if not null, hence the get(0)
+ ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getWords(composer,
+ prevWordForBigrams, proximityInfo);
+ if (null == suggestions) suggestions = new ArrayList<SuggestedWordInfo>();
+ final int length = dictionaries.size();
+ for (int i = 0; i < length; ++ i) {
+ final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getWords(composer,
+ prevWordForBigrams, proximityInfo);
+ if (null != sugg) suggestions.addAll(sugg);
+ }
+ return suggestions;
}
@Override
- public void getBigrams(final WordComposer composer, final CharSequence previousWord,
- final WordCallback callback) {
- for (final Dictionary dict : mDictionaries)
- dict.getBigrams(composer, previousWord, callback);
+ public ArrayList<SuggestedWordInfo> getBigrams(final WordComposer composer,
+ final CharSequence previousWord) {
+ final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries;
+ if (dictionaries.isEmpty()) return null;
+ // To avoid creating unnecessary objects, we get the list out of the first
+ // dictionary and add the rest to it if not null, hence the get(0)
+ ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getBigrams(composer,
+ previousWord);
+ if (null == suggestions) suggestions = new ArrayList<SuggestedWordInfo>();
+ final int length = dictionaries.size();
+ for (int i = 0; i < length; ++ i) {
+ final ArrayList<SuggestedWordInfo> sugg =
+ dictionaries.get(i).getBigrams(composer, previousWord);
+ if (null != sugg) suggestions.addAll(sugg);
+ }
+ return suggestions;
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
index a22d73af7..06a5f4b72 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -49,7 +49,8 @@ public class DictionaryFactory {
final Locale locale, final boolean useFullEditDistance) {
if (null == locale) {
Log.e(TAG, "No locale defined for dictionary");
- return new DictionaryCollection(createBinaryDictionary(context, locale));
+ return new DictionaryCollection(Dictionary.TYPE_MAIN,
+ createBinaryDictionary(context, locale));
}
final LinkedList<Dictionary> dictList = new LinkedList<Dictionary>();
@@ -59,7 +60,7 @@ public class DictionaryFactory {
for (final AssetFileAddress f : assetFileList) {
final BinaryDictionary binaryDictionary =
new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength,
- useFullEditDistance, locale);
+ useFullEditDistance, locale, Dictionary.TYPE_MAIN);
if (binaryDictionary.isValidDictionary()) {
dictList.add(binaryDictionary);
}
@@ -69,7 +70,7 @@ public class DictionaryFactory {
// If the list is empty, that means we should not use any dictionary (for example, the user
// explicitly disabled the main dictionary), so the following is okay. dictList is never
// null, but if for some reason it is, DictionaryCollection handles it gracefully.
- return new DictionaryCollection(dictList);
+ return new DictionaryCollection(Dictionary.TYPE_MAIN, dictList);
}
/**
@@ -112,7 +113,7 @@ public class DictionaryFactory {
return null;
}
return new BinaryDictionary(context, sourceDir, afd.getStartOffset(), afd.getLength(),
- false /* useFullEditDistance */, locale);
+ false /* useFullEditDistance */, locale, Dictionary.TYPE_MAIN);
} catch (android.content.res.Resources.NotFoundException e) {
Log.e(TAG, "Could not find the resource");
return null;
@@ -140,7 +141,7 @@ public class DictionaryFactory {
long startOffset, long length, final boolean useFullEditDistance, Locale locale) {
if (dictionary.isFile()) {
return new BinaryDictionary(context, dictionary.getAbsolutePath(), startOffset, length,
- useFullEditDistance, locale);
+ useFullEditDistance, locale, Dictionary.TYPE_MAIN);
} else {
Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath());
return null;
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index c65404cbc..1cda9f257 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -19,6 +19,7 @@ import android.os.SystemClock;
import android.util.Log;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
import com.android.inputmethod.latin.makedict.FusionDictionary;
import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
@@ -75,9 +76,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
/** The expandable fusion dictionary used to generate the binary dictionary. */
private FusionDictionary mFusionDictionary;
- /** The dictionary type id. */
- public final int mDicTypeId;
-
/**
* The name of this dictionary, used as the filename for storing the binary dictionary. Multiple
* dictionary instances with the same filename is supported, with access controlled by
@@ -123,11 +121,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* @param context The application context of the parent.
* @param filename The filename for this binary dictionary. Multiple dictionaries with the same
* filename is supported.
- * @param dictType The type of this dictionary.
+ * @param dictType the dictionary type, as a human-readable string
*/
public ExpandableBinaryDictionary(
- final Context context, final String filename, final int dictType) {
- mDicTypeId = dictType;
+ final Context context, final String filename, final String dictType) {
+ super(dictType);
mFilename = filename;
mContext = context;
mBinaryDictionary = null;
@@ -194,46 +192,47 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
@Override
- public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams,
- final WordCallback callback, final ProximityInfo proximityInfo) {
+ public ArrayList<SuggestedWordInfo> getWords(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
asyncReloadDictionaryIfRequired();
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
+ return getWordsInner(codes, prevWordForBigrams, proximityInfo);
}
- protected final void getWordsInner(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
+ protected final ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
// Ensure that there are no concurrent calls to getWords. If there are, do nothing and
// return.
if (mLocalDictionaryController.tryLock()) {
try {
if (mBinaryDictionary != null) {
- mBinaryDictionary.getWords(codes, prevWordForBigrams, callback, proximityInfo);
+ return mBinaryDictionary.getWords(codes, prevWordForBigrams, proximityInfo);
}
} finally {
mLocalDictionaryController.unlock();
}
}
+ return null;
}
@Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
+ public ArrayList<SuggestedWordInfo> getBigrams(final WordComposer codes,
+ final CharSequence previousWord) {
asyncReloadDictionaryIfRequired();
- getBigramsInner(codes, previousWord, callback);
+ return getBigramsInner(codes, previousWord);
}
- protected void getBigramsInner(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
+ protected ArrayList<SuggestedWordInfo> getBigramsInner(final WordComposer codes,
+ final CharSequence previousWord) {
if (mLocalDictionaryController.tryLock()) {
try {
if (mBinaryDictionary != null) {
- mBinaryDictionary.getBigrams(codes, previousWord, callback);
+ return mBinaryDictionary.getBigrams(codes, previousWord);
}
} finally {
mLocalDictionaryController.unlock();
}
}
+ return null;
}
@Override
@@ -306,7 +305,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// Build the new binary dictionary
final BinaryDictionary newBinaryDictionary =
new BinaryDictionary(mContext, filename, 0, length, true /* useFullEditDistance */,
- null);
+ null, mDictType);
if (mBinaryDictionary != null) {
// Ensure all threads accessing the current dictionary have finished before swapping in
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index f5886aa12..76213c0da 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -38,7 +38,6 @@ public class ExpandableDictionary extends Dictionary {
private Context mContext;
private char[] mWordBuilder = new char[BinaryDictionary.MAX_WORD_LENGTH];
- private int mDicTypeId;
private int mMaxDepth;
private int mInputLength;
@@ -152,11 +151,11 @@ public class ExpandableDictionary extends Dictionary {
private int[][] mCodes;
- public ExpandableDictionary(Context context, int dicTypeId) {
+ public ExpandableDictionary(final Context context, final String dictType) {
+ super(dictType);
mContext = context;
clearDictionary();
mCodes = new int[BinaryDictionary.MAX_WORD_LENGTH][];
- mDicTypeId = dicTypeId;
}
public void loadDictionary() {
@@ -248,20 +247,20 @@ public class ExpandableDictionary extends Dictionary {
}
@Override
- public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams,
- final WordCallback callback, final ProximityInfo proximityInfo) {
+ public ArrayList<SuggestedWordInfo> getWords(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
synchronized (mUpdatingLock) {
// If we need to update, start off a background task
if (mRequiresReload) startDictionaryLoadingTaskLocked();
// Currently updating contacts, don't return any results.
- if (mUpdatingDictionary) return;
+ if (mUpdatingDictionary) return null;
}
if (codes.size() >= BinaryDictionary.MAX_WORD_LENGTH) {
- return;
+ return null;
}
final ArrayList<SuggestedWordInfo> suggestions =
getWordsInner(codes, prevWordForBigrams, proximityInfo);
- Utils.addAllSuggestions(mDicTypeId, Dictionary.UNIGRAM, suggestions, callback);
+ return suggestions;
}
protected final ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes,
@@ -269,8 +268,9 @@ public class ExpandableDictionary extends Dictionary {
final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>();
mInputLength = codes.size();
if (mCodes.length < mInputLength) mCodes = new int[mInputLength][];
- final int[] xCoordinates = codes.getXCoordinates();
- final int[] yCoordinates = codes.getYCoordinates();
+ final InputPointers ips = codes.getInputPointers();
+ final int[] xCoordinates = ips.getXCoordinates();
+ final int[] yCoordinates = ips.getYCoordinates();
// Cache the codes so that we don't have to lookup an array list
for (int i = 0; i < mInputLength; i++) {
// TODO: Calculate proximity info here.
@@ -383,7 +383,7 @@ public class ExpandableDictionary extends Dictionary {
// the respective size of the typed word and the suggestion if it matters sometime
// in the future.
suggestions.add(new SuggestedWordInfo(new String(word, 0, depth + 1), finalFreq,
- SuggestedWordInfo.KIND_CORRECTION));
+ SuggestedWordInfo.KIND_CORRECTION, mDictType));
if (suggestions.size() >= Suggest.MAX_SUGGESTIONS) return false;
}
if (null != node.mShortcutTargets) {
@@ -391,7 +391,7 @@ public class ExpandableDictionary extends Dictionary {
for (int shortcutIndex = 0; shortcutIndex < length; ++shortcutIndex) {
final char[] shortcut = node.mShortcutTargets.get(shortcutIndex);
suggestions.add(new SuggestedWordInfo(new String(shortcut, 0, shortcut.length),
- finalFreq, SuggestedWordInfo.KIND_SHORTCUT));
+ finalFreq, SuggestedWordInfo.KIND_SHORTCUT, mDictType));
if (suggestions.size() > Suggest.MAX_SUGGESTIONS) return false;
}
}
@@ -600,22 +600,25 @@ public class ExpandableDictionary extends Dictionary {
}
private void runBigramReverseLookUp(final CharSequence previousWord,
- final WordCallback callback) {
+ final ArrayList<SuggestedWordInfo> suggestions) {
// Search for the lowercase version of the word only, because that's where bigrams
// store their sons.
Node prevWord = searchNode(mRoots, previousWord.toString().toLowerCase(), 0,
previousWord.length());
if (prevWord != null && prevWord.mNGrams != null) {
- reverseLookUp(prevWord.mNGrams, callback);
+ reverseLookUp(prevWord.mNGrams, suggestions);
}
}
@Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
+ public ArrayList<SuggestedWordInfo> getBigrams(final WordComposer codes,
+ final CharSequence previousWord) {
if (!reloadDictionaryIfRequired()) {
- runBigramReverseLookUp(previousWord, callback);
+ final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>();
+ runBigramReverseLookUp(previousWord, suggestions);
+ return suggestions;
}
+ return null;
}
/**
@@ -642,11 +645,12 @@ public class ExpandableDictionary extends Dictionary {
/**
* reverseLookUp retrieves the full word given a list of terminal nodes and adds those words
- * through callback.
+ * to the suggestions list passed as an argument.
* @param terminalNodes list of terminal nodes we want to add
+ * @param suggestions the suggestion collection to add the word to
*/
private void reverseLookUp(LinkedList<NextWord> terminalNodes,
- final WordCallback callback) {
+ final ArrayList<SuggestedWordInfo> suggestions) {
Node node;
int freq;
for (NextWord nextWord : terminalNodes) {
@@ -660,8 +664,9 @@ public class ExpandableDictionary extends Dictionary {
} while (node != null);
if (freq >= 0) {
- callback.addWord(mLookedUpString, index, BinaryDictionary.MAX_WORD_LENGTH - index,
- freq, mDicTypeId, Dictionary.BIGRAM);
+ suggestions.add(new SuggestedWordInfo(new String(mLookedUpString, index,
+ BinaryDictionary.MAX_WORD_LENGTH - index),
+ freq, SuggestedWordInfo.KIND_CORRECTION, mDictType));
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
new file mode 100644
index 000000000..218243e9f
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/InputPointers.java
@@ -0,0 +1,131 @@
+/*
+ * 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.latin;
+
+import java.util.Arrays;
+
+public class InputPointers {
+ private final ScalableIntArray mXCoordinates = new ScalableIntArray();
+ private final ScalableIntArray mYCoordinates = new ScalableIntArray();
+ private final ScalableIntArray mPointerIds = new ScalableIntArray();
+ private final ScalableIntArray mTimes = new ScalableIntArray();
+
+ public void addPointer(int index, int x, int y, int pointerId, int time) {
+ mXCoordinates.add(index, x);
+ mYCoordinates.add(index, y);
+ mPointerIds.add(index, pointerId);
+ mTimes.add(index, time);
+ }
+
+ public void addPointer(int x, int y, int pointerId, int time) {
+ mXCoordinates.add(x);
+ mYCoordinates.add(y);
+ mPointerIds.add(pointerId);
+ mTimes.add(time);
+ }
+
+ public void set(InputPointers ip) {
+ mXCoordinates.set(ip.mXCoordinates);
+ mYCoordinates.set(ip.mYCoordinates);
+ mPointerIds.set(ip.mPointerIds);
+ mTimes.set(ip.mTimes);
+ }
+
+ public void copy(InputPointers ip) {
+ mXCoordinates.copy(ip.mXCoordinates);
+ mYCoordinates.copy(ip.mYCoordinates);
+ mPointerIds.copy(ip.mPointerIds);
+ mTimes.copy(ip.mTimes);
+ }
+
+ public void reset() {
+ mXCoordinates.reset();
+ mYCoordinates.reset();
+ mPointerIds.reset();
+ mTimes.reset();
+ }
+
+ public int getPointerSize() {
+ return mXCoordinates.getLength();
+ }
+
+ public int[] getXCoordinates() {
+ return mXCoordinates.mArray;
+ }
+
+ public int[] getYCoordinates() {
+ return mYCoordinates.mArray;
+ }
+
+ public int[] getPointerIds() {
+ return mPointerIds.mArray;
+ }
+
+ public int[] getTimes() {
+ return mTimes.mArray;
+ }
+
+ private static class ScalableIntArray {
+ private static final int DEFAULT_SIZE = BinaryDictionary.MAX_WORD_LENGTH;
+ private int[] mArray;
+ private int mLength;
+
+ public ScalableIntArray() {
+ reset();
+ }
+
+ public void add(int index, int val) {
+ if (mLength < index + 1) {
+ mLength = index;
+ add(val);
+ } else {
+ mArray[index] = val;
+ }
+ }
+
+ public void add(int val) {
+ if (mLength >= mArray.length) {
+ final int[] newArray = new int[mLength * 2];
+ System.arraycopy(mArray, 0, newArray, 0, mLength);
+ }
+ mArray[mLength] = val;
+ ++mLength;
+ }
+
+ public int getLength() {
+ return mLength;
+ }
+
+ public void reset() {
+ mArray = new int[DEFAULT_SIZE];
+ mLength = 0;
+ }
+
+ public int[] getPrimitiveArray() {
+ return mArray;
+ }
+
+ public void copy(ScalableIntArray ip) {
+ mArray = Arrays.copyOf(ip.mArray, ip.mArray.length);
+ }
+
+ public void set(ScalableIntArray ip) {
+ mArray = ip.mArray;
+ mLength = ip.mLength;
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java
index 4e1f5fe92..318aecb50 100644
--- a/java/src/com/android/inputmethod/latin/LastComposedWord.java
+++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java
@@ -41,26 +41,26 @@ public class LastComposedWord {
public static final int NOT_A_SEPARATOR = -1;
public final int[] mPrimaryKeyCodes;
- public final int[] mXCoordinates;
- public final int[] mYCoordinates;
public final String mTypedWord;
public final String mCommittedWord;
public final int mSeparatorCode;
public final CharSequence mPrevWord;
+ public final InputPointers mInputPointers = new InputPointers();
private boolean mActive;
public static final LastComposedWord NOT_A_COMPOSED_WORD =
- new LastComposedWord(null, null, null, "", "", NOT_A_SEPARATOR, null);
+ new LastComposedWord(null, null, "", "", NOT_A_SEPARATOR, null);
// Warning: this is using the passed objects as is and fully expects them to be
// immutable. Do not fiddle with their contents after you passed them to this constructor.
- public LastComposedWord(final int[] primaryKeyCodes, final int[] xCoordinates,
- final int[] yCoordinates, final String typedWord, final String committedWord,
+ public LastComposedWord(final int[] primaryKeyCodes, final InputPointers inputPointers,
+ final String typedWord, final String committedWord,
final int separatorCode, final CharSequence prevWord) {
mPrimaryKeyCodes = primaryKeyCodes;
- mXCoordinates = xCoordinates;
- mYCoordinates = yCoordinates;
+ if (inputPointers != null) {
+ mInputPointers.copy(inputPointers);
+ }
mTypedWord = typedWord;
mCommittedWord = committedWord;
mSeparatorCode = separatorCode;
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 5cbf40400..21f0ea007 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -471,8 +471,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
// is not guaranteed. It may even be called at the same time on a different thread.
if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
- mUserHistoryDictionary = UserHistoryDictionary.getInstance(
- this, localeStr, Suggest.DIC_USER_HISTORY, mPrefs);
+ mUserHistoryDictionary = UserHistoryDictionary.getInstance(this, localeStr, mPrefs);
mSuggest.setUserHistoryDictionary(mUserHistoryDictionary);
}
@@ -500,8 +499,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// If the locale has changed then recreate the contacts dictionary. This
// allows locale dependent rules for handling bigram name predictions.
oldContactsDictionary.close();
- dictionaryToUse = new ContactsBinaryDictionary(
- this, Suggest.DIC_CONTACTS, locale);
+ dictionaryToUse = new ContactsBinaryDictionary(this, locale);
} else {
// Make sure the old contacts dictionary is opened. If it is already open,
// this is a no-op, so it's safe to call it anyways.
@@ -509,7 +507,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
dictionaryToUse = oldContactsDictionary;
}
} else {
- dictionaryToUse = new ContactsBinaryDictionary(this, Suggest.DIC_CONTACTS, locale);
+ dictionaryToUse = new ContactsBinaryDictionary(this, locale);
}
}
@@ -890,7 +888,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
applicationSuggestedWords,
false /* typedWordValid */,
false /* hasAutoCorrectionCandidate */,
- false /* allowsToBeAutoCorrected */,
false /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */,
false /* isPrediction */);
@@ -1718,9 +1715,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return;
}
- mHandler.cancelUpdateSuggestions();
- mHandler.cancelUpdateBigramPredictions();
-
if (!mWordComposer.isComposingWord()) {
// We are never called with an empty word composer, but if because of a bug
// we are, what we should do here is just call updateBigramsPredictions. This will
@@ -1736,7 +1730,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// getSuggestedWords handles gracefully a null value of prevWord
final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(),
- mCurrentSettings.mCorrectionEnabled);
+ mCurrentSettings.mCorrectionEnabled, false);
// Basically, we update the suggestion strip only when suggestion count > 1. However,
// there is an exception: We update the suggestion strip whenever typed word's length
@@ -1745,7 +1739,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// need to clear the previous state when the user starts typing a word (i.e. typed word's
// length == 1).
if (suggestedWords.size() > 1 || typedWord.length() == 1
- || !suggestedWords.mAllowsToBeAutoCorrected
+ || !suggestedWords.mTypedWordValid
|| mSuggestionsView.isShowingAddToDictionaryHint()) {
showSuggestions(suggestedWords, typedWord);
} else {
@@ -1760,7 +1754,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
new SuggestedWords(typedWordAndPreviousSuggestions,
false /* typedWordValid */,
false /* hasAutoCorrectionCandidate */,
- false /* allowsToBeAutoCorrected */,
false /* isPunctuationSuggestions */,
true /* isObsoleteSuggestions */,
false /* isPrediction */);
@@ -1771,7 +1764,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void showSuggestions(final SuggestedWords suggestedWords, final CharSequence typedWord) {
final CharSequence autoCorrection;
if (suggestedWords.size() > 0) {
- if (suggestedWords.hasAutoCorrectionWord()) {
+ if (suggestedWords.mWillAutoCorrect) {
autoCorrection = suggestedWords.getWord(1);
} else {
autoCorrection = typedWord;
@@ -1936,8 +1929,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
public void updateBigramPredictions() {
- if (mSuggest == null || !mCurrentSettings.isSuggestionsRequested(mDisplayOrientation))
+ mHandler.cancelUpdateSuggestions();
+ mHandler.cancelUpdateBigramPredictions();
+
+ if (mSuggest == null || !mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
+ if (mWordComposer.isComposingWord()) {
+ Log.w(TAG, "Called updateBigramPredictions but suggestions were not requested!");
+ mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
+ }
return;
+ }
if (!mCurrentSettings.mBigramPredictionEnabled) {
setPunctuationSuggestions();
@@ -1948,7 +1949,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mCurrentSettings.mCorrectionEnabled) {
final CharSequence prevWord = mConnection.getThisWord(mCurrentSettings.mWordSeparators);
if (!TextUtils.isEmpty(prevWord)) {
- suggestedWords = mSuggest.getBigramPredictions(prevWord);
+ suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
+ prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(),
+ mCurrentSettings.mCorrectionEnabled, true);
} else {
suggestedWords = null;
}
diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
index dc0868e7c..e843848bc 100644
--- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java
+++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
@@ -71,7 +71,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang
public static void onStartSuggestion(CharSequence previousWords) {
}
- public static void onAddSuggestedWord(String word, int typeId, int dataType) {
+ public static void onAddSuggestedWord(String word, String sourceDictionaryId) {
}
public static void onSetKeyboard(Keyboard kb) {
diff --git a/java/src/com/android/inputmethod/latin/ResearchLogger.java b/java/src/com/android/inputmethod/latin/ResearchLogger.java
index cf3cc7873..e83d7c84a 100644
--- a/java/src/com/android/inputmethod/latin/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/latin/ResearchLogger.java
@@ -197,6 +197,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
Log.d(TAG, "stop called");
if (mLoggingHandler != null && mLoggingState == LOGGING_STATE_ON) {
mLoggingState = LOGGING_STATE_STOPPING;
+ flushEventQueue(true);
// put this in the Handler queue so pending writes are processed first.
mLoggingHandler.post(new Runnable() {
@Override
@@ -379,11 +380,52 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
mCurrentLogUnit.addLogAtom(keys, values, false);
}
+ // Used to track how often words are logged. Too-frequent logging can leak
+ // semantics, disclosing private data.
+ /* package for test */ static class LoggingFrequencyState {
+ private static final int DEFAULT_WORD_LOG_FREQUENCY = 10;
+ private int mWordsRemainingToSkip;
+ private final int mFrequency;
+
+ /**
+ * Tracks how often words may be uploaded.
+ *
+ * @param frequency 1=Every word, 2=Every other word, etc.
+ */
+ public LoggingFrequencyState(int frequency) {
+ mFrequency = frequency;
+ mWordsRemainingToSkip = mFrequency;
+ }
+
+ public void onWordLogged() {
+ mWordsRemainingToSkip = mFrequency;
+ }
+
+ public void onWordNotLogged() {
+ if (mWordsRemainingToSkip > 1) {
+ mWordsRemainingToSkip--;
+ }
+ }
+
+ public boolean isSafeToLog() {
+ return mWordsRemainingToSkip <= 1;
+ }
+ }
+
+ /* package for test */ LoggingFrequencyState mLoggingFrequencyState =
+ new LoggingFrequencyState(LoggingFrequencyState.DEFAULT_WORD_LOG_FREQUENCY);
+
/* package for test */ boolean isPrivacyThreat(String word) {
- // currently: word not in dictionary or contains numbers.
+ // Current checks:
+ // - Word not in dictionary
+ // - Word contains numbers
+ // - Privacy-safe word not logged recently
if (TextUtils.isEmpty(word)) {
return false;
}
+ if (!mLoggingFrequencyState.isSafeToLog()) {
+ return true;
+ }
final int length = word.length();
boolean hasLetter = false;
for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
@@ -410,15 +452,26 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
return false;
}
+ private void onWordComplete(String word) {
+ final boolean isPrivacyThreat = isPrivacyThreat(word);
+ flushEventQueue(isPrivacyThreat);
+ if (isPrivacyThreat) {
+ mLoggingFrequencyState.onWordNotLogged();
+ } else {
+ mLoggingFrequencyState.onWordLogged();
+ }
+ }
+
/**
* Write out enqueued LogEvents to the log, possibly dropping privacy sensitive events.
*/
- /* package for test */ synchronized void flushQueue(boolean removePotentiallyPrivateEvents) {
+ /* package for test */ synchronized void flushEventQueue(
+ boolean removePotentiallyPrivateEvents) {
if (isAllowedToLog()) {
mCurrentLogUnit.setRemovePotentiallyPrivateEvents(removePotentiallyPrivateEvents);
mLoggingHandler.post(mCurrentLogUnit);
- mCurrentLogUnit = new LogUnit();
}
+ mCurrentLogUnit = new LogUnit();
}
private synchronized void outputEvent(final String[] keys, final Object[] values) {
@@ -479,12 +532,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
SuggestedWords words = (SuggestedWords) value;
mJsonWriter.beginObject();
mJsonWriter.name("typedWordValid").value(words.mTypedWordValid);
- mJsonWriter.name("hasAutoCorrectionCandidate")
- .value(words.mHasAutoCorrectionCandidate);
+ mJsonWriter.name("willAutoCorrect").value(words.mWillAutoCorrect);
mJsonWriter.name("isPunctuationSuggestions")
.value(words.mIsPunctuationSuggestions);
- mJsonWriter.name("allowsToBeAutoCorrected")
- .value(words.mAllowsToBeAutoCorrected);
mJsonWriter.name("isObsoleteSuggestions")
.value(words.mIsObsoleteSuggestions);
mJsonWriter.name("isPrediction")
@@ -652,7 +702,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
final ResearchLogger researchLogger = getInstance();
researchLogger.enqueuePotentiallyPrivateEvent(
EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values);
- researchLogger.flushQueue(researchLogger.isPrivacyThreat(autoCorrection));
}
private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = {
@@ -665,7 +714,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
};
final ResearchLogger researchLogger = getInstance();
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values);
- researchLogger.flushQueue(researchLogger.isPrivacyThreat(scrubbedWord));
+ researchLogger.onWordComplete(scrubbedWord);
}
private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = {
@@ -743,7 +792,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
final ResearchLogger researchLogger = getInstance();
researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values);
- researchLogger.flushQueue(true); // Play it safe. Remove privacy-sensitive events.
+ researchLogger.flushEventQueue(true); // Play it safe. Remove privacy-sensitive events.
}
}
@@ -824,7 +873,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
final ResearchLogger researchLogger = getInstance();
researchLogger.enqueuePotentiallyPrivateEvent(
EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, values);
- researchLogger.flushQueue(researchLogger.isPrivacyThreat(cs.toString()));
}
private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = {
@@ -839,7 +887,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
final ResearchLogger researchLogger = getInstance();
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY,
values);
- researchLogger.flushQueue(researchLogger.isPrivacyThreat(suggestion.toString()));
}
private static final String[] EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION = {
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 0c19bed05..40d327ebb 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -340,13 +340,6 @@ public class RichInputConnection {
* Returns the word before the cursor if the cursor is at the end of a word, null otherwise
*/
public CharSequence getWordBeforeCursorIfAtEndOfWord(final SettingsValues settings) {
- // Bail out if the cursor is not at the end of a word (cursor must be preceded by
- // non-whitespace, non-separator, non-start-of-text)
- // Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here.
- final CharSequence textBeforeCursor = getTextBeforeCursor(1, 0);
- if (TextUtils.isEmpty(textBeforeCursor)
- || settings.isWordSeparator(textBeforeCursor.charAt(0))) return null;
-
// Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace,
// separator or end of line/text)
// Example: "test|"<EOL> "te|st" get rejected here
@@ -363,6 +356,15 @@ public class RichInputConnection {
word = word.subSequence(1, word.length());
}
if (TextUtils.isEmpty(word)) return null;
+ // Find the last code point of the string
+ final int lastCodePoint = Character.codePointBefore(word, word.length());
+ // If for some reason the text field contains non-unicode binary data, or if the
+ // charsequence is exactly one char long and the contents is a low surrogate, return null.
+ if (!Character.isDefined(lastCodePoint)) return null;
+ // Bail out if the cursor is not at the end of a word (cursor must be preceded by
+ // non-whitespace, non-separator, non-start-of-text)
+ // Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here.
+ if (settings.isWordSeparator(lastCodePoint)) return null;
final char firstChar = word.charAt(0); // we just tested that word is not empty
if (word.length() == 1 && !Character.isLetter(firstChar)) return null;
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 4c67b4957..4c89a6e91 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -143,19 +143,39 @@ public class Settings extends InputMethodSettingsFragment
generalSettings.removePreference(mVoicePreference);
}
+ final PreferenceGroup advancedSettings =
+ (PreferenceGroup) findPreference(PREF_ADVANCED_SETTINGS);
if (!VibratorUtils.getInstance(context).hasVibrator()) {
- final PreferenceGroup advancedSettings =
- (PreferenceGroup) findPreference(PREF_ADVANCED_SETTINGS);
generalSettings.removePreference(findPreference(PREF_VIBRATE_ON));
if (null != advancedSettings) { // Theoretically advancedSettings cannot be null
advancedSettings.removePreference(findPreference(PREF_VIBRATION_DURATION_SETTINGS));
}
}
- final boolean showPopupOption = res.getBoolean(
+ final boolean showKeyPreviewPopupOption = res.getBoolean(
R.bool.config_enable_show_popup_on_keypress_option);
- if (!showPopupOption) {
+ mKeyPreviewPopupDismissDelay =
+ (ListPreference) findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
+ if (!showKeyPreviewPopupOption) {
generalSettings.removePreference(findPreference(PREF_POPUP_ON));
+ if (null != advancedSettings) { // Theoretically advancedSettings cannot be null
+ advancedSettings.removePreference(mKeyPreviewPopupDismissDelay);
+ }
+ } else {
+ final String[] entries = new String[] {
+ res.getString(R.string.key_preview_popup_dismiss_no_delay),
+ res.getString(R.string.key_preview_popup_dismiss_default_delay),
+ };
+ final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
+ R.integer.config_key_preview_linger_timeout));
+ mKeyPreviewPopupDismissDelay.setEntries(entries);
+ mKeyPreviewPopupDismissDelay.setEntryValues(
+ new String[] { "0", popupDismissDelayDefaultValue });
+ if (null == mKeyPreviewPopupDismissDelay.getValue()) {
+ mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
+ }
+ mKeyPreviewPopupDismissDelay.setEnabled(
+ SettingsValues.isKeyPreviewPopupEnabled(prefs, res));
}
final CheckBoxPreference includeOtherImesInLanguageSwitchList =
@@ -163,23 +183,6 @@ public class Settings extends InputMethodSettingsFragment
includeOtherImesInLanguageSwitchList.setEnabled(
!SettingsValues.isLanguageSwitchKeySupressed(prefs));
- mKeyPreviewPopupDismissDelay =
- (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
- final String[] entries = new String[] {
- res.getString(R.string.key_preview_popup_dismiss_no_delay),
- res.getString(R.string.key_preview_popup_dismiss_default_delay),
- };
- final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
- R.integer.config_key_preview_linger_timeout));
- mKeyPreviewPopupDismissDelay.setEntries(entries);
- mKeyPreviewPopupDismissDelay.setEntryValues(
- new String[] { "0", popupDismissDelayDefaultValue });
- if (null == mKeyPreviewPopupDismissDelay.getValue()) {
- mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
- }
- mKeyPreviewPopupDismissDelay.setEnabled(
- SettingsValues.isKeyPreviewPopupEnabled(prefs, res));
-
final PreferenceScreen dictionaryLink =
(PreferenceScreen) findPreference(PREF_CONFIGURE_DICTIONARIES_KEY);
final Intent intent = dictionaryLink.getIntent();
@@ -305,13 +308,15 @@ public class Settings extends InputMethodSettingsFragment
private void updateKeyPreviewPopupDelaySummary() {
final ListPreference lp = mKeyPreviewPopupDismissDelay;
- lp.setSummary(lp.getEntries()[lp.findIndexOfValue(lp.getValue())]);
+ final CharSequence[] entries = lp.getEntries();
+ if (entries == null || entries.length <= 0) return;
+ lp.setSummary(entries[lp.findIndexOfValue(lp.getValue())]);
}
private void updateVoiceModeSummary() {
mVoicePreference.setSummary(
getResources().getStringArray(R.array.voice_input_modes_summary)
- [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]);
+ [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]);
}
private void refreshEnablingsOfKeypressSoundAndVibrationSettings(
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index ef423f19b..aab84fccd 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -179,13 +179,13 @@ public class SettingsValues {
if (puncs != null) {
for (final String puncSpec : puncs) {
puncList.add(new SuggestedWordInfo(KeySpecParser.getLabel(puncSpec),
- SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_HARDCODED));
+ SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_HARDCODED,
+ Dictionary.TYPE_HARDCODED));
}
}
return new SuggestedWords(puncList,
false /* typedWordValid */,
false /* hasAutoCorrectionCandidate */,
- false /* allowsToBeAutoCorrected */,
true /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */,
false /* isPrediction */);
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index a43b90525..6e7d985d6 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -184,6 +184,9 @@ public class StringUtils {
final char[] characters = string.toCharArray();
final int length = characters.length;
final int[] codePoints = new int[Character.codePointCount(characters, 0, length)];
+ if (length <= 0) {
+ return new int[0];
+ }
int codePoint = Character.codePointAt(characters, 0);
int dsti = 0;
for (int srci = Character.charCount(codePoint);
diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
index ca293060a..acc17ef3f 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
@@ -60,6 +60,10 @@ public class SubtypeLocale {
// Exceptional locales to display name map.
private static final HashMap<String, String> sExceptionalDisplayNamesMap =
new HashMap<String, String>();
+ // Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value.
+ // This is for compatibility to keep the same subtype ids as pre-JellyBean.
+ private static final HashMap<String,String> sLocaleAndExtraValueToKeyboardLayoutSetMap =
+ new HashMap<String,String>();
private SubtypeLocale() {
// Intentional empty constructor for utility class.
@@ -97,6 +101,14 @@ public class SubtypeLocale {
final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
sExceptionalLocaleToWithLayoutNameIdsMap.put(localeString, resId);
}
+
+ final String[] keyboardLayoutSetMap = res.getStringArray(
+ R.array.locale_and_extra_value_to_keyboard_layout_set_map);
+ for (int i = 0; i < keyboardLayoutSetMap.length; i += 2) {
+ final String key = keyboardLayoutSetMap[i];
+ final String keyboardLayoutSet = keyboardLayoutSetMap[i + 1];
+ sLocaleAndExtraValueToKeyboardLayoutSetMap.put(key, keyboardLayoutSet);
+ }
}
public static String[] getPredefinedKeyboardLayoutSet() {
@@ -193,7 +205,14 @@ public class SubtypeLocale {
}
public static String getKeyboardLayoutSetName(InputMethodSubtype subtype) {
- final String keyboardLayoutSet = subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET);
+ String keyboardLayoutSet = subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET);
+ if (keyboardLayoutSet == null) {
+ // This subtype doesn't have a keyboardLayoutSet extra value, so lookup its keyboard
+ // layout set in sLocaleAndExtraValueToKeyboardLayoutSetMap to keep it compatible with
+ // pre-JellyBean.
+ final String key = subtype.getLocale() + ":" + subtype.getExtraValue();
+ keyboardLayoutSet = sLocaleAndExtraValueToKeyboardLayoutSetMap.get(key);
+ }
// TODO: Remove this null check when InputMethodManager.getCurrentInputMethodSubtype is
// fixed.
if (keyboardLayoutSet == null) {
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 5aecc1371..f810eccf4 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -18,7 +18,6 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.text.TextUtils;
-import android.util.Log;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.ProximityInfo;
@@ -26,6 +25,7 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import java.io.File;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
@@ -34,80 +34,47 @@ import java.util.concurrent.ConcurrentHashMap;
* This class loads a dictionary and provides a list of suggestions for a given sequence of
* characters. This includes corrections and completions.
*/
-public class Suggest implements Dictionary.WordCallback {
+public class Suggest {
public static final String TAG = Suggest.class.getSimpleName();
- public static final int APPROX_MAX_WORD_LENGTH = 32;
-
// TODO: rename this to CORRECTION_OFF
public static final int CORRECTION_NONE = 0;
// TODO: rename this to CORRECTION_ON
public static final int CORRECTION_FULL = 1;
- // It seems the following values are only used for logging.
- public static final int DIC_USER_TYPED = 0;
- public static final int DIC_MAIN = 1;
- public static final int DIC_USER = 2;
- public static final int DIC_USER_HISTORY = 3;
- public static final int DIC_CONTACTS = 4;
- public static final int DIC_WHITELIST = 6;
- // If you add a type of dictionary, increment DIC_TYPE_LAST_ID
- // TODO: this value seems unused. Remove it?
- public static final int DIC_TYPE_LAST_ID = 6;
- public static final String DICT_KEY_MAIN = "main";
- public static final String DICT_KEY_CONTACTS = "contacts";
- // User dictionary, the system-managed one.
- public static final String DICT_KEY_USER = "user";
- // User history dictionary for the unigram map, internal to LatinIME
- public static final String DICT_KEY_USER_HISTORY_UNIGRAM = "history_unigram";
- // User history dictionary for the bigram map, internal to LatinIME
- public static final String DICT_KEY_USER_HISTORY_BIGRAM = "history_bigram";
- public static final String DICT_KEY_WHITELIST ="whitelist";
-
private static final boolean DBG = LatinImeLogger.sDBG;
private Dictionary mMainDictionary;
private ContactsBinaryDictionary mContactsDict;
private WhitelistDictionary mWhiteListDictionary;
- private final ConcurrentHashMap<String, Dictionary> mUnigramDictionaries =
- new ConcurrentHashMap<String, Dictionary>();
- private final ConcurrentHashMap<String, Dictionary> mBigramDictionaries =
+ private final ConcurrentHashMap<String, Dictionary> mDictionaries =
new ConcurrentHashMap<String, Dictionary>();
public static final int MAX_SUGGESTIONS = 18;
- private static final int PREF_MAX_BIGRAMS = 60;
-
private float mAutoCorrectionThreshold;
- private ArrayList<SuggestedWordInfo> mSuggestions = new ArrayList<SuggestedWordInfo>();
- private ArrayList<SuggestedWordInfo> mBigramSuggestions = new ArrayList<SuggestedWordInfo>();
- private CharSequence mConsideredWord;
-
- // TODO: Remove these member variables by passing more context to addWord() callback method
- private boolean mIsFirstCharCapitalized;
- private boolean mIsAllUpperCase;
- private int mTrailingSingleQuotesCount;
-
- private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4;
+ // Locale used for upper- and title-casing words
+ final private Locale mLocale;
public Suggest(final Context context, final Locale locale) {
initAsynchronously(context, locale);
+ mLocale = locale;
}
/* package for test */ Suggest(final Context context, final File dictionary,
final long startOffset, final long length, final Locale locale) {
final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(context, dictionary,
startOffset, length /* useFullEditDistance */, false, locale);
+ mLocale = locale;
mMainDictionary = mainDict;
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict);
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, mainDict);
initWhitelistAndAutocorrectAndPool(context, locale);
}
private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) {
mWhiteListDictionary = new WhitelistDictionary(context, locale);
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary);
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_WHITELIST, mWhiteListDictionary);
}
private void initAsynchronously(final Context context, final Locale locale) {
@@ -136,8 +103,7 @@ public class Suggest implements Dictionary.WordCallback {
public void run() {
final DictionaryCollection newMainDict =
DictionaryFactory.createMainDictionaryFromManager(context, locale);
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, newMainDict);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, newMainDict);
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, newMainDict);
mMainDictionary = newMainDict;
}
}.start();
@@ -158,11 +124,7 @@ public class Suggest implements Dictionary.WordCallback {
}
public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() {
- return mUnigramDictionaries;
- }
-
- public static int getApproxMaxWordLength() {
- return APPROX_MAX_WORD_LENGTH;
+ return mDictionaries;
}
/**
@@ -170,7 +132,7 @@ public class Suggest implements Dictionary.WordCallback {
* before the main dictionary, if set. This refers to the system-managed user dictionary.
*/
public void setUserDictionary(UserBinaryDictionary userDictionary) {
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER, userDictionary);
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER, userDictionary);
}
/**
@@ -180,213 +142,147 @@ public class Suggest implements Dictionary.WordCallback {
*/
public void setContactsDictionary(ContactsBinaryDictionary contactsDictionary) {
mContactsDict = contactsDictionary;
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_CONTACTS, contactsDictionary);
}
public void setUserHistoryDictionary(UserHistoryDictionary userHistoryDictionary) {
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER_HISTORY_UNIGRAM,
- userHistoryDictionary);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_USER_HISTORY_BIGRAM,
- userHistoryDictionary);
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER_HISTORY, userHistoryDictionary);
}
public void setAutoCorrectionThreshold(float threshold) {
mAutoCorrectionThreshold = threshold;
}
- private static CharSequence capitalizeWord(final boolean all, final boolean first,
- final CharSequence word) {
- if (TextUtils.isEmpty(word) || !(all || first)) return word;
- final int wordLength = word.length();
- final StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
- // TODO: Must pay attention to locale when changing case.
- if (all) {
- sb.append(word.toString().toUpperCase());
- } else if (first) {
- sb.append(Character.toUpperCase(word.charAt(0)));
- if (wordLength > 1) {
- sb.append(word.subSequence(1, wordLength));
- }
- }
- return sb;
- }
-
- protected void addBigramToSuggestions(SuggestedWordInfo bigram) {
- mSuggestions.add(bigram);
- }
-
- private static final WordComposer sEmptyWordComposer = new WordComposer();
- public SuggestedWords getBigramPredictions(CharSequence prevWordForBigram) {
- LatinImeLogger.onStartSuggestion(prevWordForBigram);
- mIsFirstCharCapitalized = false;
- mIsAllUpperCase = false;
- mTrailingSingleQuotesCount = 0;
- mSuggestions = new ArrayList<SuggestedWordInfo>(MAX_SUGGESTIONS);
-
- // Treating USER_TYPED as UNIGRAM suggestion for logging now.
- LatinImeLogger.onAddSuggestedWord("", Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM);
- mConsideredWord = "";
-
- mBigramSuggestions = new ArrayList<SuggestedWordInfo>(PREF_MAX_BIGRAMS);
-
- getAllBigrams(prevWordForBigram, sEmptyWordComposer);
-
- // Nothing entered: return all bigrams for the previous word
- int insertCount = Math.min(mBigramSuggestions.size(), MAX_SUGGESTIONS);
- for (int i = 0; i < insertCount; ++i) {
- addBigramToSuggestions(mBigramSuggestions.get(i));
- }
-
- SuggestedWordInfo.removeDups(mSuggestions);
-
- return new SuggestedWords(mSuggestions,
- false /* typedWordValid */,
- false /* hasAutoCorrectionCandidate */,
- false /* allowsToBeAutoCorrected */,
- false /* isPunctuationSuggestions */,
- false /* isObsoleteSuggestions */,
- true /* isPrediction */);
- }
-
// TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder
public SuggestedWords getSuggestedWords(
final WordComposer wordComposer, CharSequence prevWordForBigram,
- final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) {
+ final ProximityInfo proximityInfo, final boolean isCorrectionEnabled,
+ // TODO: remove isPrediction parameter. It effectively means the same thing
+ // as wordComposer.size() <= 1
+ final boolean isPrediction) {
LatinImeLogger.onStartSuggestion(prevWordForBigram);
- mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
- mIsAllUpperCase = wordComposer.isAllUpperCase();
- mTrailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
- mSuggestions = new ArrayList<SuggestedWordInfo>(MAX_SUGGESTIONS);
+ final boolean isFirstCharCapitalized =
+ !isPrediction && wordComposer.isFirstCharCapitalized();
+ final boolean isAllUpperCase = !isPrediction && wordComposer.isAllUpperCase();
+ final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
+ final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
+ MAX_SUGGESTIONS);
final String typedWord = wordComposer.getTypedWord();
- final String consideredWord = mTrailingSingleQuotesCount > 0
- ? typedWord.substring(0, typedWord.length() - mTrailingSingleQuotesCount)
+ final String consideredWord = trailingSingleQuotesCount > 0
+ ? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount)
: typedWord;
- // Treating USER_TYPED as UNIGRAM suggestion for logging now.
- LatinImeLogger.onAddSuggestedWord(typedWord, Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM);
- mConsideredWord = consideredWord;
+ LatinImeLogger.onAddSuggestedWord(typedWord, Dictionary.TYPE_USER_TYPED);
if (wordComposer.size() <= 1 && isCorrectionEnabled) {
// At first character typed, search only the bigrams
- mBigramSuggestions = new ArrayList<SuggestedWordInfo>(PREF_MAX_BIGRAMS);
-
if (!TextUtils.isEmpty(prevWordForBigram)) {
for (final String key : mDictionaries.keySet()) {
final Dictionary dictionary = mDictionaries.get(key);
suggestionsSet.addAll(dictionary.getBigrams(wordComposer, prevWordForBigram));
}
}
-
} else if (wordComposer.size() > 1) {
final WordComposer wordComposerForLookup;
- if (mTrailingSingleQuotesCount > 0) {
+ if (trailingSingleQuotesCount > 0) {
wordComposerForLookup = new WordComposer(wordComposer);
- for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
+ for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
wordComposerForLookup.deleteLast();
}
} else {
wordComposerForLookup = wordComposer;
}
// At second character typed, search the unigrams (scores being affected by bigrams)
- for (final String key : mUnigramDictionaries.keySet()) {
+ for (final String key : mDictionaries.keySet()) {
// Skip UserUnigramDictionary and WhitelistDictionary to lookup
- if (key.equals(DICT_KEY_USER_HISTORY_UNIGRAM) || key.equals(DICT_KEY_WHITELIST))
+ if (key.equals(Dictionary.TYPE_USER_HISTORY)
+ || key.equals(Dictionary.TYPE_WHITELIST))
continue;
- final Dictionary dictionary = mUnigramDictionaries.get(key);
- dictionary.getWords(wordComposerForLookup, prevWordForBigram, this, proximityInfo);
+ final Dictionary dictionary = mDictionaries.get(key);
+ suggestionsSet.addAll(dictionary.getWords(
+ wordComposerForLookup, prevWordForBigram, proximityInfo));
}
}
- final CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase,
- mIsFirstCharCapitalized, mWhiteListDictionary.getWhitelistedWord(consideredWord));
+ // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
+ // but still autocorrected from - in the case the whitelist only capitalizes the word.
+ // The whitelist should be case-insensitive, so it's not possible to be consistent with
+ // a boolean flag. Right now this is handled with a slight hack in
+ // WhitelistDictionary#shouldForciblyAutoCorrectFrom.
+ final boolean allowsToBeAutoCorrected = AutoCorrection.isWhitelistedOrNotAWord(
+ mDictionaries, consideredWord, wordComposer.isFirstCharCapitalized());
+
+ final CharSequence whitelistedWord =
+ mWhiteListDictionary.getWhitelistedWord(consideredWord);
final boolean hasAutoCorrection;
- if (isCorrectionEnabled) {
- final CharSequence autoCorrection =
- AutoCorrection.computeAutoCorrectionWord(mUnigramDictionaries, wordComposer,
- mSuggestions, consideredWord, mAutoCorrectionThreshold,
- whitelistedWord);
- hasAutoCorrection = (null != autoCorrection);
+ if (!isCorrectionEnabled || !allowsToBeAutoCorrected || wordComposer.isMostlyCaps()
+ || wordComposer.isResumed() || !hasMainDictionary()) {
+ // If we don't have a main dictionary, we never want to auto-correct. The reason for
+ // this is, the user may have a contact whose name happens to match a valid word in
+ // their language, and it will unexpectedly auto-correct. For example, if the user
+ // types in English with no dictionary and has a "Will" in their contact list, "will"
+ // would always auto-correct to "Will" which is unwanted. Hence, no main dict => no
+ // auto-correct.
+ hasAutoCorrection = false;
+ } else if (null != whitelistedWord) {
+ hasAutoCorrection = true;
+ } else if (suggestionsSet.isEmpty()) {
+ hasAutoCorrection = false;
+ } else if (AutoCorrection.suggestionExceedsAutoCorrectionThreshold(suggestionsSet.first(),
+ consideredWord, mAutoCorrectionThreshold)) {
+ hasAutoCorrection = true;
} else {
hasAutoCorrection = false;
}
if (whitelistedWord != null) {
- if (mTrailingSingleQuotesCount > 0) {
- final StringBuilder sb = new StringBuilder(whitelistedWord);
- for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
- sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
- }
- mSuggestions.add(0, new SuggestedWordInfo(sb.toString(),
- SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_WHITELIST));
- } else {
- mSuggestions.add(0, new SuggestedWordInfo(whitelistedWord,
- SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_WHITELIST));
+ suggestionsSet.add(new SuggestedWordInfo(whitelistedWord,
+ SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_WHITELIST,
+ Dictionary.TYPE_WHITELIST));
+ }
+
+ final ArrayList<SuggestedWordInfo> suggestionsContainer =
+ new ArrayList<SuggestedWordInfo>(suggestionsSet);
+ final int suggestionsCount = suggestionsContainer.size();
+ if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) {
+ for (int i = 0; i < suggestionsCount; ++i) {
+ final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
+ final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
+ wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized,
+ trailingSingleQuotesCount);
+ suggestionsContainer.set(i, transformedWordInfo);
}
}
- mSuggestions.add(0, new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE,
- SuggestedWordInfo.KIND_TYPED));
- SuggestedWordInfo.removeDups(mSuggestions);
+ for (int i = 0; i < suggestionsCount; ++i) {
+ final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
+ LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict);
+ }
+
+ if (!TextUtils.isEmpty(typedWord)) {
+ suggestionsContainer.add(0, new SuggestedWordInfo(typedWord,
+ SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED,
+ Dictionary.TYPE_USER_TYPED));
+ }
+ SuggestedWordInfo.removeDups(suggestionsContainer);
final ArrayList<SuggestedWordInfo> suggestionsList;
- if (DBG) {
- suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, mSuggestions);
+ if (DBG && !suggestionsContainer.isEmpty()) {
+ suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, suggestionsContainer);
} else {
- suggestionsList = mSuggestions;
+ suggestionsList = suggestionsContainer;
}
- // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
- // but still autocorrected from - in the case the whitelist only capitalizes the word.
- // The whitelist should be case-insensitive, so it's not possible to be consistent with
- // a boolean flag. Right now this is handled with a slight hack in
- // WhitelistDictionary#shouldForciblyAutoCorrectFrom.
- final boolean allowsToBeAutoCorrected = AutoCorrection.allowsToBeAutoCorrected(
- getUnigramDictionaries(), consideredWord, wordComposer.isFirstCharCapitalized())
- // If we don't have a main dictionary, we never want to auto-correct. The reason for this
- // is, the user may have a contact whose name happens to match a valid word in their
- // language, and it will unexpectedly auto-correct. For example, if the user types in
- // English with no dictionary and has a "Will" in their contact list, "will" would
- // always auto-correct to "Will" which is unwanted. Hence, no main dict => no auto-correct.
- && hasMainDictionary();
-
- boolean autoCorrectionAvailable = hasAutoCorrection;
- if (isCorrectionEnabled) {
- autoCorrectionAvailable |= !allowsToBeAutoCorrected;
- }
- // Don't auto-correct words with multiple capital letter
- autoCorrectionAvailable &= !wordComposer.isMostlyCaps();
- autoCorrectionAvailable &= !wordComposer.isResumed();
- if (allowsToBeAutoCorrected && suggestionsList.size() > 1 && mAutoCorrectionThreshold > 0
- && Suggest.shouldBlockAutoCorrectionBySafetyNet(typedWord,
- suggestionsList.get(1).mWord)) {
- autoCorrectionAvailable = false;
- }
return new SuggestedWords(suggestionsList,
- !allowsToBeAutoCorrected /* typedWordValid */,
- autoCorrectionAvailable /* hasAutoCorrectionCandidate */,
- allowsToBeAutoCorrected /* allowsToBeAutoCorrected */,
+ // TODO: this first argument is lying. If this is a whitelisted word which is an
+ // actual word, it says typedWordValid = false, which looks wrong. We should either
+ // rename the attribute or change the value.
+ !isPrediction && !allowsToBeAutoCorrected /* typedWordValid */,
+ !isPrediction && hasAutoCorrection, /* willAutoCorrect */
false /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */,
- false /* isPrediction */);
- }
-
- /**
- * Adds all bigram predictions for prevWord. Also checks the lower case version of prevWord if
- * it contains any upper case characters.
- */
- private void getAllBigrams(final CharSequence prevWord, final WordComposer wordComposer) {
- if (StringUtils.hasUpperCase(prevWord)) {
- // TODO: Must pay attention to locale when changing case.
- final CharSequence lowerPrevWord = prevWord.toString().toLowerCase();
- for (final Dictionary dictionary : mBigramDictionaries.values()) {
- dictionary.getBigrams(wordComposer, lowerPrevWord, this);
- }
- }
- for (final Dictionary dictionary : mBigramDictionaries.values()) {
- dictionary.getBigrams(wordComposer, prevWord, this);
- }
+ isPrediction);
}
private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo(
@@ -415,120 +311,44 @@ public class Suggest implements Dictionary.WordCallback {
return suggestionsList;
}
- // TODO: Use codepoint instead of char
- @Override
- public boolean addWord(final char[] word, final int offset, final int length, int score,
- final int dicTypeId, final int dataType) {
- int dataTypeForLog = dataType;
- final ArrayList<SuggestedWordInfo> suggestions;
- final int prefMaxSuggestions;
- if (dataType == Dictionary.BIGRAM) {
- suggestions = mBigramSuggestions;
- prefMaxSuggestions = PREF_MAX_BIGRAMS;
- } else {
- suggestions = mSuggestions;
- prefMaxSuggestions = MAX_SUGGESTIONS;
+ private static class SuggestedWordInfoComparator implements Comparator<SuggestedWordInfo> {
+ // This comparator ranks the word info with the higher frequency first. That's because
+ // that's the order we want our elements in.
+ @Override
+ public int compare(final SuggestedWordInfo o1, final SuggestedWordInfo o2) {
+ if (o1.mScore > o2.mScore) return -1;
+ if (o1.mScore < o2.mScore) return 1;
+ if (o1.mCodePointCount < o2.mCodePointCount) return -1;
+ if (o1.mCodePointCount > o2.mCodePointCount) return 1;
+ return o1.mWord.toString().compareTo(o2.mWord.toString());
}
-
- int pos = 0;
-
- // Check if it's the same word, only caps are different
- if (StringUtils.equalsIgnoreCase(mConsideredWord, word, offset, length)) {
- // TODO: remove this surrounding if clause and move this logic to
- // getSuggestedWordBuilder.
- if (suggestions.size() > 0) {
- final SuggestedWordInfo currentHighestWord = suggestions.get(0);
- // If the current highest word is also equal to typed word, we need to compare
- // frequency to determine the insertion position. This does not ensure strictly
- // correct ordering, but ensures the top score is on top which is enough for
- // removing duplicates correctly.
- if (StringUtils.equalsIgnoreCase(currentHighestWord.mWord, word, offset, length)
- && score <= currentHighestWord.mScore) {
- pos = 1;
- }
- }
- } else {
- // Check the last one's score and bail
- if (suggestions.size() >= prefMaxSuggestions
- && suggestions.get(prefMaxSuggestions - 1).mScore >= score) return true;
- while (pos < suggestions.size()) {
- final int curScore = suggestions.get(pos).mScore;
- if (curScore < score
- || (curScore == score && length < suggestions.get(pos).codePointCount())) {
- break;
- }
- pos++;
- }
- }
- if (pos >= prefMaxSuggestions) {
- return true;
- }
-
- final StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
- // TODO: Must pay attention to locale when changing case.
- if (mIsAllUpperCase) {
- sb.append(new String(word, offset, length).toUpperCase());
- } else if (mIsFirstCharCapitalized) {
- sb.append(Character.toUpperCase(word[offset]));
- if (length > 1) {
- sb.append(word, offset + 1, length - 1);
- }
+ }
+ private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator =
+ new SuggestedWordInfoComparator();
+
+ private static SuggestedWordInfo getTransformedSuggestedWordInfo(
+ final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase,
+ final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) {
+ final StringBuilder sb = new StringBuilder(wordInfo.mWord.length());
+ if (isAllUpperCase) {
+ sb.append(wordInfo.mWord.toString().toUpperCase(locale));
+ } else if (isFirstCharCapitalized) {
+ sb.append(StringUtils.toTitleCase(wordInfo.mWord.toString(), locale));
} else {
- sb.append(word, offset, length);
+ sb.append(wordInfo.mWord);
}
- for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
+ for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
}
- // TODO: figure out what type of suggestion this is
- suggestions.add(pos, new SuggestedWordInfo(sb, score, SuggestedWordInfo.KIND_CORRECTION));
- if (suggestions.size() > prefMaxSuggestions) {
- suggestions.remove(prefMaxSuggestions);
- } else {
- LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog);
- }
- return true;
+ return new SuggestedWordInfo(sb, wordInfo.mScore, wordInfo.mKind, wordInfo.mSourceDict);
}
public void close() {
final HashSet<Dictionary> dictionaries = new HashSet<Dictionary>();
- dictionaries.addAll(mUnigramDictionaries.values());
- dictionaries.addAll(mBigramDictionaries.values());
+ dictionaries.addAll(mDictionaries.values());
for (final Dictionary dictionary : dictionaries) {
dictionary.close();
}
mMainDictionary = null;
}
-
- // TODO: Resolve the inconsistencies between the native auto correction algorithms and
- // this safety net
- public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord,
- final CharSequence suggestion) {
- // Safety net for auto correction.
- // Actually if we hit this safety net, it's a bug.
- // If user selected aggressive auto correction mode, there is no need to use the safety
- // net.
- // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH,
- // we should not use net because relatively edit distance can be big.
- final int typedWordLength = typedWord.length();
- if (typedWordLength < Suggest.MINIMUM_SAFETY_NET_CHAR_LENGTH) {
- return false;
- }
- final int maxEditDistanceOfNativeDictionary =
- (typedWordLength < 5 ? 2 : typedWordLength / 2) + 1;
- final int distance = BinaryDictionary.editDistance(typedWord, suggestion.toString());
- if (DBG) {
- Log.d(TAG, "Autocorrected edit distance = " + distance
- + ", " + maxEditDistanceOfNativeDictionary);
- }
- if (distance > maxEditDistanceOfNativeDictionary) {
- if (DBG) {
- Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestion);
- Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. "
- + "Turning off auto-correction.");
- }
- return true;
- } else {
- return false;
- }
- }
}
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 45ac9ff53..f079c2112 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -25,27 +25,27 @@ import java.util.HashSet;
public class SuggestedWords {
public static final SuggestedWords EMPTY = new SuggestedWords(
- new ArrayList<SuggestedWordInfo>(0), false, false, false, false, false, false);
+ new ArrayList<SuggestedWordInfo>(0), false, false, false, false, false);
public final boolean mTypedWordValid;
- public final boolean mHasAutoCorrectionCandidate;
+ // Note: this INCLUDES cases where the word will auto-correct to itself. A good definition
+ // of what this flag means would be "the top suggestion is strong enough to auto-correct",
+ // whether this exactly matches the user entry or not.
+ public final boolean mWillAutoCorrect;
public final boolean mIsPunctuationSuggestions;
- public final boolean mAllowsToBeAutoCorrected;
public final boolean mIsObsoleteSuggestions;
public final boolean mIsPrediction;
private final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList;
public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
final boolean typedWordValid,
- final boolean hasAutoCorrectionCandidate,
- final boolean allowsToBeAutoCorrected,
+ final boolean willAutoCorrect,
final boolean isPunctuationSuggestions,
final boolean isObsoleteSuggestions,
final boolean isPrediction) {
mSuggestedWordInfoList = suggestedWordInfoList;
mTypedWordValid = typedWordValid;
- mHasAutoCorrectionCandidate = hasAutoCorrectionCandidate;
- mAllowsToBeAutoCorrected = allowsToBeAutoCorrected;
+ mWillAutoCorrect = willAutoCorrect;
mIsPunctuationSuggestions = isPunctuationSuggestions;
mIsObsoleteSuggestions = isObsoleteSuggestions;
mIsPrediction = isPrediction;
@@ -55,7 +55,7 @@ public class SuggestedWords {
return mSuggestedWordInfoList.size();
}
- public CharSequence getWord(int pos) {
+ public String getWord(int pos) {
return mSuggestedWordInfoList.get(pos).mWord;
}
@@ -67,12 +67,8 @@ public class SuggestedWords {
return mSuggestedWordInfoList.get(pos);
}
- public boolean hasAutoCorrectionWord() {
- return mHasAutoCorrectionCandidate && size() > 1 && !mTypedWordValid;
- }
-
public boolean willAutoCorrect() {
- return !mTypedWordValid && mHasAutoCorrectionCandidate;
+ return mWillAutoCorrect;
}
@Override
@@ -80,8 +76,7 @@ public class SuggestedWords {
// Pretty-print method to help debug
return "SuggestedWords:"
+ " mTypedWordValid=" + mTypedWordValid
- + " mHasAutoCorrectionCandidate=" + mHasAutoCorrectionCandidate
- + " mAllowsToBeAutoCorrected=" + mAllowsToBeAutoCorrected
+ + " mWillAutoCorrect=" + mWillAutoCorrect
+ " mIsPunctuationSuggestions=" + mIsPunctuationSuggestions
+ " words=" + Arrays.toString(mSuggestedWordInfoList.toArray());
}
@@ -92,7 +87,7 @@ public class SuggestedWords {
for (CompletionInfo info : infos) {
if (null != info && info.getText() != null) {
result.add(new SuggestedWordInfo(info.getText(), SuggestedWordInfo.MAX_SCORE,
- SuggestedWordInfo.KIND_APP_DEFINED));
+ SuggestedWordInfo.KIND_APP_DEFINED, Dictionary.TYPE_APPLICATION_DEFINED));
}
}
return result;
@@ -105,7 +100,7 @@ public class SuggestedWords {
final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<SuggestedWordInfo>();
final HashSet<String> alreadySeen = new HashSet<String>();
suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE,
- SuggestedWordInfo.KIND_TYPED));
+ SuggestedWordInfo.KIND_TYPED, Dictionary.TYPE_USER_TYPED));
alreadySeen.add(typedWord.toString());
final int previousSize = previousSuggestions.size();
for (int pos = 1; pos < previousSize; pos++) {
@@ -130,19 +125,20 @@ public class SuggestedWords {
public static final int KIND_HARDCODED = 5; // Hardcoded suggestion, e.g. punctuation
public static final int KIND_APP_DEFINED = 6; // Suggested by the application
public static final int KIND_SHORTCUT = 7; // A shortcut
- private final String mWordStr;
- public final CharSequence mWord;
+ public final String mWord;
public final int mScore;
public final int mKind; // one of the KIND_* constants above
public final int mCodePointCount;
+ public final String mSourceDict;
private String mDebugString = "";
- public SuggestedWordInfo(final CharSequence word, final int score, final int kind) {
- mWordStr = word.toString();
- mWord = word;
+ public SuggestedWordInfo(final CharSequence word, final int score, final int kind,
+ final String sourceDict) {
+ mWord = word.toString();
mScore = score;
mKind = kind;
- mCodePointCount = mWordStr.codePointCount(0, mWordStr.length());
+ mSourceDict = sourceDict;
+ mCodePointCount = StringUtils.codePointCount(mWord);
}
@@ -160,15 +156,15 @@ public class SuggestedWords {
}
public int codePointAt(int i) {
- return mWordStr.codePointAt(i);
+ return mWord.codePointAt(i);
}
@Override
public String toString() {
if (TextUtils.isEmpty(mDebugString)) {
- return mWordStr;
+ return mWord;
} else {
- return mWordStr + " (" + mDebugString.toString() + ")";
+ return mWord + " (" + mDebugString.toString() + ")";
}
}
@@ -182,7 +178,7 @@ public class SuggestedWords {
final SuggestedWordInfo cur = candidates.get(i);
for (int j = 0; j < i; ++j) {
final SuggestedWordInfo previous = candidates.get(j);
- if (TextUtils.equals(cur.mWord, previous.mWord)) {
+ if (cur.mWord.equals(previous.mWord)) {
candidates.remove(cur.mScore < previous.mScore ? i : j);
--i;
break;
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
index 673b54500..9b20bd690 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
@@ -19,22 +19,23 @@ package com.android.inputmethod.latin;
import android.content.Context;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import java.util.ArrayList;
import java.util.Locale;
public class SynchronouslyLoadedContactsBinaryDictionary extends ContactsBinaryDictionary {
private boolean mClosed;
public SynchronouslyLoadedContactsBinaryDictionary(final Context context, final Locale locale) {
- super(context, Suggest.DIC_CONTACTS, locale);
+ super(context, locale);
}
@Override
- public synchronized void getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
+ public synchronized ArrayList<SuggestedWordInfo> getWords(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
syncReloadDictionaryIfRequired();
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
+ return getWordsInner(codes, prevWordForBigrams, proximityInfo);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
index 1606a34e0..5b2a6edec 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
@@ -19,6 +19,9 @@ package com.android.inputmethod.latin;
import android.content.Context;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
+import java.util.ArrayList;
public class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDictionary {
@@ -32,11 +35,10 @@ public class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDictionar
}
@Override
- public synchronized void getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
+ public synchronized ArrayList<SuggestedWordInfo> getWords(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
syncReloadDictionaryIfRequired();
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
+ return getWordsInner(codes, prevWordForBigrams, proximityInfo);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index 5bcdb57b5..60e6fa127 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -34,7 +34,10 @@ import java.util.Arrays;
*/
public class UserBinaryDictionary extends ExpandableBinaryDictionary {
- // TODO: use Words.SHORTCUT when it's public in the SDK
+ // The user dictionary provider uses an empty string to mean "all languages".
+ private static final String USER_DICTIONARY_ALL_LANGUAGES = "";
+
+ // TODO: use Words.SHORTCUT when we target JellyBean or above
final static String SHORTCUT = "shortcut";
private static final String[] PROJECTION_QUERY;
static {
@@ -69,9 +72,14 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
public UserBinaryDictionary(final Context context, final String locale,
final boolean alsoUseMoreRestrictiveLocales) {
- super(context, getFilenameWithLocale(NAME, locale), Suggest.DIC_USER);
+ super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_USER);
if (null == locale) throw new NullPointerException(); // Catch the error earlier
- mLocale = locale;
+ if (SubtypeLocale.NO_LANGUAGE.equals(locale)) {
+ // If we don't have a locale, insert into the "all locales" user dictionary.
+ mLocale = USER_DICTIONARY_ALL_LANGUAGES;
+ } else {
+ mLocale = locale;
+ }
mAlsoUseMoreRestrictiveLocales = alsoUseMoreRestrictiveLocales;
// Perform a managed query. The Activity will handle closing and re-querying the cursor
// when needed.
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
index 5095f6582..73fa83f9a 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
@@ -115,8 +115,7 @@ public class UserHistoryDictionary extends ExpandableDictionary {
}
public synchronized static UserHistoryDictionary getInstance(
- final Context context, final String locale,
- final int dictTypeId, final SharedPreferences sp) {
+ final Context context, final String locale, final SharedPreferences sp) {
if (sLangDictCache.containsKey(locale)) {
final SoftReference<UserHistoryDictionary> ref = sLangDictCache.get(locale);
final UserHistoryDictionary dict = ref == null ? null : ref.get();
@@ -128,14 +127,14 @@ public class UserHistoryDictionary extends ExpandableDictionary {
}
}
final UserHistoryDictionary dict =
- new UserHistoryDictionary(context, locale, dictTypeId, sp);
+ new UserHistoryDictionary(context, locale, sp);
sLangDictCache.put(locale, new SoftReference<UserHistoryDictionary>(dict));
return dict;
}
- private UserHistoryDictionary(final Context context, final String locale, final int dicTypeId,
- SharedPreferences sp) {
- super(context, dicTypeId);
+ private UserHistoryDictionary(final Context context, final String locale,
+ final SharedPreferences sp) {
+ super(context, Dictionary.TYPE_USER_HISTORY);
mLocale = locale;
mPrefs = sp;
if (sOpenHelper == null) {
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index a44b1f9ad..8f71de0e7 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -531,14 +531,4 @@ public class Utils {
}
return builder.toString();
}
-
- public static void addAllSuggestions(final int dicTypeId, final int dataType,
- final ArrayList<SuggestedWords.SuggestedWordInfo> suggestions,
- final Dictionary.WordCallback callback) {
- for (SuggestedWordInfo suggestion : suggestions) {
- final String suggestionStr = suggestion.mWord.toString();
- callback.addWord(suggestionStr.toCharArray(), 0, suggestionStr.length(),
- suggestion.mScore, dicTypeId, dataType);
- }
- }
}
diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
index a0de2f970..3af22140e 100644
--- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
+++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
@@ -37,7 +37,7 @@ public class WhitelistDictionary extends ExpandableDictionary {
// TODO: Conform to the async load contact of ExpandableDictionary
public WhitelistDictionary(final Context context, final Locale locale) {
- super(context, Suggest.DIC_WHITELIST);
+ super(context, Dictionary.TYPE_WHITELIST);
// TODO: Move whitelist dictionary into main dictionary.
final RunInLocale<Void> job = new RunInLocale<Void>() {
@Override
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 43b92337a..bf5e05e9b 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -33,8 +33,7 @@ public class WordComposer {
private static final int N = BinaryDictionary.MAX_WORD_LENGTH;
private int[] mPrimaryKeyCodes;
- private int[] mXCoordinates;
- private int[] mYCoordinates;
+ private final InputPointers mInputPointers = new InputPointers();
private StringBuilder mTypedWord;
private CharSequence mAutoCorrection;
private boolean mIsResumed;
@@ -53,8 +52,6 @@ public class WordComposer {
public WordComposer() {
mPrimaryKeyCodes = new int[N];
mTypedWord = new StringBuilder(N);
- mXCoordinates = new int[N];
- mYCoordinates = new int[N];
mAutoCorrection = null;
mTrailingSingleQuotesCount = 0;
mIsResumed = false;
@@ -68,8 +65,7 @@ public class WordComposer {
public void init(WordComposer source) {
mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length);
mTypedWord = new StringBuilder(source.mTypedWord);
- mXCoordinates = Arrays.copyOf(source.mXCoordinates, source.mXCoordinates.length);
- mYCoordinates = Arrays.copyOf(source.mYCoordinates, source.mYCoordinates.length);
+ mInputPointers.copy(source.mInputPointers);
mCapsCount = source.mCapsCount;
mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
mAutoCapitalized = source.mAutoCapitalized;
@@ -91,7 +87,7 @@ public class WordComposer {
refreshSize();
}
- public final void refreshSize() {
+ private final void refreshSize() {
mCodePointSize = mTypedWord.codePointCount(0, mTypedWord.length());
}
@@ -115,12 +111,8 @@ public class WordComposer {
return mPrimaryKeyCodes[index];
}
- public int[] getXCoordinates() {
- return mXCoordinates;
- }
-
- public int[] getYCoordinates() {
- return mYCoordinates;
+ public InputPointers getInputPointers() {
+ return mInputPointers;
}
private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) {
@@ -138,8 +130,8 @@ public class WordComposer {
if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) {
mPrimaryKeyCodes[newIndex] = primaryCode >= Keyboard.CODE_SPACE
? Character.toLowerCase(primaryCode) : primaryCode;
- mXCoordinates[newIndex] = keyX;
- mYCoordinates[newIndex] = keyY;
+ // TODO: Set correct pointer id and time
+ mInputPointers.addPointer(newIndex, keyX, keyY, 0, 0);
}
mIsFirstCharCapitalized = isFirstCharCapitalized(
newIndex, primaryCode, mIsFirstCharCapitalized);
@@ -298,14 +290,11 @@ public class WordComposer {
// or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
// the last composed word to ensure this does not happen.
final int[] primaryKeyCodes = mPrimaryKeyCodes;
- final int[] xCoordinates = mXCoordinates;
- final int[] yCoordinates = mYCoordinates;
mPrimaryKeyCodes = new int[N];
- mXCoordinates = new int[N];
- mYCoordinates = new int[N];
final LastComposedWord lastComposedWord = new LastComposedWord(primaryKeyCodes,
- xCoordinates, yCoordinates, mTypedWord.toString(), committedWord, separatorCode,
+ mInputPointers, mTypedWord.toString(), committedWord, separatorCode,
prevWord);
+ mInputPointers.reset();
if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD
&& type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) {
lastComposedWord.deactivate();
@@ -319,8 +308,7 @@ public class WordComposer {
public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes;
- mXCoordinates = lastComposedWord.mXCoordinates;
- mYCoordinates = lastComposedWord.mYCoordinates;
+ mInputPointers.set(lastComposedWord.mInputPointers);
mTypedWord.setLength(0);
mTypedWord.append(lastComposedWord.mTypedWord);
refreshSize();
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 69b48cecc..5332c066a 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -32,12 +32,12 @@ import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.BinaryDictionary;
import com.android.inputmethod.latin.ContactsBinaryDictionary;
import com.android.inputmethod.latin.Dictionary;
-import com.android.inputmethod.latin.Dictionary.WordCallback;
import com.android.inputmethod.latin.DictionaryCollection;
import com.android.inputmethod.latin.DictionaryFactory;
import com.android.inputmethod.latin.LocaleUtils;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StringUtils;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.SynchronouslyLoadedContactsBinaryDictionary;
import com.android.inputmethod.latin.SynchronouslyLoadedUserBinaryDictionary;
import com.android.inputmethod.latin.UserBinaryDictionary;
@@ -203,7 +203,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService
EMPTY_STRING_ARRAY);
}
- private static class SuggestionsGatherer implements WordCallback {
+ // TODO: remove this class and replace it by storage local to the session.
+ private static class SuggestionsGatherer {
public static class Result {
public final String[] mSuggestions;
public final boolean mHasRecommendedSuggestions;
@@ -237,9 +238,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService
mScores = new int[mMaxLength];
}
- @Override
- synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score,
- int dicTypeId, int dataType) {
+ synchronized public boolean addWord(char[] word, int[] spaceIndices, int wordOffset,
+ int wordLength, int score) {
final int positionIndex = Arrays.binarySearch(mScores, 0, mLength, score);
// binarySearch returns the index if the element exists, and -<insertion index> - 1
// if it doesn't. See documentation for binarySearch.
@@ -780,8 +780,13 @@ public class AndroidSpellCheckerService extends SpellCheckerService
try {
dictInfo = mDictionaryPool.takeOrGetNull();
if (null == dictInfo) return getNotInDictEmptySuggestions();
- dictInfo.mDictionary.getWords(composer, prevWord, suggestionsGatherer,
- dictInfo.mProximityInfo);
+ final ArrayList<SuggestedWordInfo> suggestions = dictInfo.mDictionary.getWords(
+ composer, prevWord, dictInfo.mProximityInfo);
+ for (final SuggestedWordInfo suggestion : suggestions) {
+ final String suggestionStr = suggestion.mWord.toString();
+ suggestionsGatherer.addWord(suggestionStr.toCharArray(), null, 0,
+ suggestionStr.length(), suggestion.mScore);
+ }
isInDict = dictInfo.mDictionary.isValidWord(text);
if (!isInDict && CAPITALIZE_NONE != capitalizeType) {
// We want to test the word again if it's all caps or first caps only.
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java
index e86390b11..642a551ce 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java
@@ -57,11 +57,11 @@ import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.keyboard.MoreKeysPanel;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.keyboard.ViewLayoutUtils;
+import com.android.inputmethod.latin.AutoCorrection;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.ResearchLogger;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
-import com.android.inputmethod.latin.Suggest;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.Utils;
import com.android.inputmethod.latin.define.ProductionFlag;
@@ -336,8 +336,8 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
if (LatinImeLogger.sDBG && suggestedWords.size() > 1) {
// If we auto-correct, then the autocorrection is in slot 0 and the typed word
// is in slot 1.
- if (index == mCenterSuggestionIndex && suggestedWords.mHasAutoCorrectionCandidate
- && Suggest.shouldBlockAutoCorrectionBySafetyNet(
+ if (index == mCenterSuggestionIndex
+ && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet(
suggestedWords.getWord(1).toString(), suggestedWords.getWord(0))) {
return 0xFFFF0000;
}