aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod')
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java12
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java31
-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.java37
-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.java458
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java34
-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, 721 insertions, 828 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index 5ffd94a43..23acb8b74 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -19,7 +19,6 @@ 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;
@@ -40,8 +39,8 @@ public class KeyCodeDescriptionMapper {
// Map of key labels to spoken description resource IDs
private final HashMap<CharSequence, Integer> mKeyLabelMap;
- // Sparse array of spoken description resource IDs indexed by key codes
- private final SparseIntArray mKeyCodeMap;
+ // Map of key codes to spoken description resource IDs
+ private final HashMap<Integer, Integer> mKeyCodeMap;
public static void init() {
sInstance.initInternal();
@@ -53,7 +52,7 @@ public class KeyCodeDescriptionMapper {
private KeyCodeDescriptionMapper() {
mKeyLabelMap = new HashMap<CharSequence, Integer>();
- mKeyCodeMap = new SparseIntArray();
+ mKeyCodeMap = new HashMap<Integer, Integer>();
}
private void initInternal() {
@@ -61,7 +60,7 @@ public class KeyCodeDescriptionMapper {
mKeyLabelMap.put(":-)", R.string.spoken_description_smiley);
// Symbols that most TTS engines can't speak
- mKeyCodeMap.put(' ', R.string.spoken_description_space);
+ mKeyCodeMap.put((int) ' ', R.string.spoken_description_space);
// Special non-character codes defined in Keyboard
mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete);
@@ -274,8 +273,7 @@ public class KeyCodeDescriptionMapper {
return context.getString(OBSCURED_KEY_RES_ID);
}
- final int resId = mKeyCodeMap.get(code);
- if (resId != 0) {
+ if (mKeyCodeMap.containsKey(code)) {
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 1b4cea2e7..6fc630d05 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -23,8 +23,6 @@ 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;
@@ -46,6 +44,7 @@ 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;
@@ -135,7 +134,7 @@ public class Keyboard {
public final Key[] mAltCodeKeysWhileTyping;
public final KeyboardIconsSet mIconsSet;
- private final SparseArray<Key> mKeyCache = new SparseArray<Key>();
+ private final HashMap<Integer, Key> mKeyCache = new HashMap<Integer, Key>();
private final ProximityInfo mProximityInfo;
private final boolean mProximityCharsCorrectionEnabled;
@@ -185,23 +184,23 @@ public class Keyboard {
if (code == CODE_UNSPECIFIED) {
return null;
}
- final int index = mKeyCache.indexOfKey(code);
- if (index >= 0) {
- return mKeyCache.valueAt(index);
+ final Integer keyCode = code;
+ if (mKeyCache.containsKey(keyCode)) {
+ return mKeyCache.get(keyCode);
}
for (final Key key : mKeys) {
if (key.mCode == code) {
- mKeyCache.put(code, key);
+ mKeyCache.put(keyCode, key);
return key;
}
}
- mKeyCache.put(code, null);
+ mKeyCache.put(keyCode, null);
return null;
}
public boolean hasKey(Key aKey) {
- if (mKeyCache.indexOfValue(aKey) >= 0) {
+ if (mKeyCache.containsKey(aKey)) {
return true;
}
@@ -345,8 +344,8 @@ public class Keyboard {
private int mMaxHeightCount = 0;
private int mMaxWidthCount = 0;
- private final SparseIntArray mHeightHistogram = new SparseIntArray();
- private final SparseIntArray mWidthHistogram = new SparseIntArray();
+ private final HashMap<Integer, Integer> mHeightHistogram = new HashMap<Integer, Integer>();
+ private final HashMap<Integer, Integer> mWidthHistogram = new HashMap<Integer, Integer>();
private void clearHistogram() {
mMostCommonKeyHeight = 0;
@@ -358,22 +357,22 @@ public class Keyboard {
mWidthHistogram.clear();
}
- private static int updateHistogramCounter(SparseIntArray histogram, int key) {
- final int index = histogram.indexOfKey(key);
- final int count = (index >= 0 ? histogram.get(key) : 0) + 1;
+ private static int updateHistogramCounter(HashMap<Integer, Integer> histogram,
+ Integer key) {
+ final int count = (histogram.containsKey(key) ? histogram.get(key) : 0) + 1;
histogram.put(key, count);
return count;
}
private void updateHistogram(Key key) {
- final int height = key.mHeight + key.mVerticalGap;
+ final Integer height = key.mHeight + key.mVerticalGap;
final int heightCount = updateHistogramCounter(mHeightHistogram, height);
if (heightCount > mMaxHeightCount) {
mMaxHeightCount = heightCount;
mMostCommonKeyHeight = height;
}
- final int width = key.mWidth + key.mHorizontalGap;
+ final Integer 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 aab89a3e5..8c7246855 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -29,7 +29,6 @@ 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;
@@ -117,9 +116,9 @@ public class KeyboardLayoutSet {
InputMethodSubtype mSubtype;
int mOrientation;
int mWidth;
- // Sparse array of KeyboardLayoutSet element parameters indexed by element's id.
- final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
- new SparseArray<ElementParams>();
+ // KeyboardLayoutSet element id to element's parameters map.
+ final HashMap<Integer, ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
+ new HashMap<Integer, 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 fb98af3e6..18e01fb49 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -30,7 +30,6 @@ 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;
@@ -43,6 +42,7 @@ 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,10 +124,12 @@ 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 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>();
+ // 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>();
private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' };
private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' };
@@ -764,7 +766,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
private final Rect mTextBounds = new Rect();
private float getCharHeight(char[] referenceChar, Paint paint) {
- final int key = getCharGeometryCacheKey(referenceChar[0], paint);
+ final Integer key = getCharGeometryCacheKey(referenceChar[0], paint);
final Float cachedValue = sTextHeightCache.get(key);
if (cachedValue != null)
return cachedValue;
@@ -776,7 +778,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
private float getCharWidth(char[] referenceChar, Paint paint) {
- final int key = getCharGeometryCacheKey(referenceChar[0], paint);
+ final Integer 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 7714ba892..383298de9 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -127,6 +127,7 @@ 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);
@@ -139,11 +140,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
final PointerTracker tracker = (PointerTracker) msg.obj;
switch (msg.what) {
case MSG_REPEAT_KEY:
- final Key currentKey = tracker.getKey();
- if (currentKey != null && currentKey.mCode == msg.arg1) {
- tracker.onRegisterKey(currentKey);
- startKeyRepeatTimer(tracker, mParams.mKeyRepeatInterval);
- }
+ tracker.onRegisterKey(tracker.getKey());
+ startKeyRepeatTimer(tracker, mParams.mKeyRepeatInterval);
break;
case MSG_LONGPRESS_KEY:
if (tracker != null) {
@@ -160,23 +158,22 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
private void startKeyRepeatTimer(PointerTracker tracker, long delay) {
- final Key key = tracker.getKey();
- if (key == null) return;
- sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
+ sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, 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 hasMessages(MSG_REPEAT_KEY);
+ return mInKeyRepeat;
}
@Override
@@ -454,8 +451,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
*/
@Override
public void setKeyboard(Keyboard keyboard) {
- // Remove any pending messages, except dismissing preview and key repeat.
- mKeyTimerHandler.cancelLongPressTimer();
+ // Remove any pending messages, except dismissing preview
+ mKeyTimerHandler.cancelKeyTimers();
super.setKeyboard(keyboard);
mKeyDetector.setKeyboard(
keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
@@ -758,18 +755,15 @@ 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, motionEvent);
+ tracker.onMoveEvent(px, py, eventTime);
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 32ef408b4..34e428e82 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -148,6 +148,9 @@ 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;
@@ -316,13 +319,6 @@ 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;
}
@@ -469,7 +465,7 @@ public class PointerTracker {
onUpEvent(x, y, eventTime);
break;
case MotionEvent.ACTION_MOVE:
- onMoveEvent(x, y, eventTime, null);
+ onMoveEvent(x, y, eventTime);
break;
case MotionEvent.ACTION_CANCEL:
onCancelEvent(x, y, eventTime);
@@ -525,6 +521,7 @@ public class PointerTracker {
|| mKeyDetector.alwaysAllowsSlidingInput();
mKeyboardLayoutHasBeenChanged = false;
mKeyAlreadyProcessed = false;
+ mIsRepeatableKey = false;
mIsInSlidingKeyInput = false;
mIgnoreModifierKey = false;
if (key != null) {
@@ -548,7 +545,7 @@ public class PointerTracker {
mIsInSlidingKeyInput = true;
}
- public void onMoveEvent(int x, int y, long eventTime, MotionEvent me) {
+ public void onMoveEvent(int x, int y, long eventTime) {
if (DEBUG_MOVE_EVENT)
printTouchEvent("onMoveEvent:", x, y, eventTime);
if (mKeyAlreadyProcessed)
@@ -671,7 +668,7 @@ public class PointerTracker {
}
if (mKeyAlreadyProcessed)
return;
- if (mCurrentKey != null && !mCurrentKey.isRepeatable()) {
+ if (!mIsRepeatableKey) {
detectAndSendKey(mCurrentKey, mKeyX, mKeyY);
}
}
@@ -717,6 +714,9 @@ public class PointerTracker {
if (key != null && key.isRepeatable()) {
onRegisterKey(key);
mTimerProxy.startKeyRepeatTimer(this);
+ mIsRepeatableKey = true;
+ } else {
+ mIsRepeatableKey = false;
}
}
@@ -760,10 +760,14 @@ 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, eventTime, code));
+ (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, delta, code));
+ mPreviousEventTime = eventTime;
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index ae123e29a..1bc825479 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -24,6 +24,7 @@ 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
@@ -189,6 +190,10 @@ 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 291b3b943..80f4f259b 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
@@ -18,7 +18,6 @@ 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;
@@ -90,7 +89,7 @@ public class KeyStyles {
private class DeclaredKeyStyle extends KeyStyle {
private final String mParentStyleName;
- private final SparseArray<Object> mStyleAttributes = new SparseArray<Object>();
+ private final HashMap<Integer, Object> mStyleAttributes = new HashMap<Integer, Object>();
public DeclaredKeyStyle(String parentStyleName) {
mParentStyleName = parentStyleName;
@@ -101,9 +100,8 @@ public class KeyStyles {
if (a.hasValue(index)) {
return parseStringArray(a, index);
}
- final Object value = mStyleAttributes.get(index);
- if (value != null) {
- return (String[])value;
+ if (mStyleAttributes.containsKey(index)) {
+ return (String[])mStyleAttributes.get(index);
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getStringArray(a, index);
@@ -114,9 +112,8 @@ public class KeyStyles {
if (a.hasValue(index)) {
return parseString(a, index);
}
- final Object value = mStyleAttributes.get(index);
- if (value != null) {
- return (String)value;
+ if (mStyleAttributes.containsKey(index)) {
+ return (String)mStyleAttributes.get(index);
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getString(a, index);
@@ -127,9 +124,8 @@ public class KeyStyles {
if (a.hasValue(index)) {
return a.getInt(index, defaultValue);
}
- final Object value = mStyleAttributes.get(index);
- if (value != null) {
- return (Integer)value;
+ if (mStyleAttributes.containsKey(index)) {
+ return (Integer)mStyleAttributes.get(index);
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getInt(a, index, defaultValue);
@@ -137,13 +133,12 @@ public class KeyStyles {
@Override
public int getFlag(TypedArray a, int index) {
- int flags = a.getInt(index, 0);
- final Object value = mStyleAttributes.get(index);
- if (value != null) {
- flags |= (Integer)value;
+ int value = a.getInt(index, 0);
+ if (mStyleAttributes.containsKey(index)) {
+ value |= (Integer)mStyleAttributes.get(index);
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
- return flags | parentStyle.getFlag(a, index);
+ return value | 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 5155851fe..540e63b3f 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
@@ -20,7 +20,6 @@ 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;
@@ -32,7 +31,8 @@ public class KeyboardIconsSet {
public static final int ICON_UNDEFINED = 0;
private static final int ATTR_UNDEFINED = 0;
- private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray();
+ private static final HashMap<Integer, Integer> ATTR_ID_TO_ICON_ID
+ = new HashMap<Integer, Integer>();
// Icon name to icon id map.
private static final HashMap<String, Integer> sNameToIdsMap = new HashMap<String, Integer>();
@@ -76,9 +76,7 @@ public class KeyboardIconsSet {
}
public void loadIcons(final TypedArray keyboardAttrs) {
- 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);
+ for (final Integer attrId : ATTR_ID_TO_ICON_ID.keySet()) {
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 c78974dac..e0452483c 100644
--- a/java/src/com/android/inputmethod/latin/AutoCorrection.java
+++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java
@@ -21,17 +21,34 @@ 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)) {
@@ -39,7 +56,7 @@ public class AutoCorrection {
}
final CharSequence lowerCasedWord = word.toString().toLowerCase();
for (final String key : dictionaries.keySet()) {
- if (key.equals(Dictionary.TYPE_WHITELIST)) continue;
+ if (key.equals(Suggest.DICT_KEY_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
@@ -64,7 +81,7 @@ public class AutoCorrection {
}
int maxFreq = -1;
for (final String key : dictionaries.keySet()) {
- if (key.equals(Dictionary.TYPE_WHITELIST)) continue;
+ if (key.equals(Suggest.DICT_KEY_WHITELIST)) continue;
final Dictionary dictionary = dictionaries.get(key);
if (null == dictionary) continue;
final int tempFreq = dictionary.getFrequency(word);
@@ -75,12 +92,11 @@ public class AutoCorrection {
return maxFreq;
}
- // Returns true if this is a whitelist entry, or it isn't in any dictionary.
- public static boolean isWhitelistedOrNotAWord(
+ public static boolean allowsToBeAutoCorrected(
final ConcurrentHashMap<String, Dictionary> dictionaries,
final CharSequence word, final boolean ignoreCase) {
final WhitelistDictionary whitelistDictionary =
- (WhitelistDictionary)dictionaries.get(Dictionary.TYPE_WHITELIST);
+ (WhitelistDictionary)dictionaries.get(Suggest.DICT_KEY_WHITELIST);
// If "word" is in the whitelist dictionary, it should not be auto corrected.
if (whitelistDictionary != null
&& whitelistDictionary.shouldForciblyAutoCorrectFrom(word)) {
@@ -89,18 +105,33 @@ public class AutoCorrection {
return !isValidWord(dictionaries, word, ignoreCase);
}
- public static boolean suggestionExceedsAutoCorrectionThreshold(SuggestedWordInfo suggestion,
+ 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,
CharSequence consideredWord, float autoCorrectionThreshold) {
- if (null != suggestion) {
+ if (wordComposer.size() > 1 && suggestions.size() > 0) {
+ final SuggestedWordInfo autoCorrectionSuggestion = suggestions.get(0);
//final int autoCorrectionSuggestionScore = sortedScores[0];
- final int autoCorrectionSuggestionScore = suggestion.mScore;
+ final int autoCorrectionSuggestionScore = autoCorrectionSuggestion.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(), suggestion.mWord.toString(),
+ consideredWord.toString(), autoCorrectionSuggestion.mWord.toString(),
autoCorrectionSuggestionScore);
if (DBG) {
- Log.d(TAG, "Normalized " + consideredWord + "," + suggestion + ","
+ Log.d(TAG, "Normalized " + consideredWord + "," + autoCorrectionSuggestion + ","
+ autoCorrectionSuggestionScore + ", " + normalizedScore
+ "(" + autoCorrectionThreshold + ")");
}
@@ -108,43 +139,10 @@ public class AutoCorrection {
if (DBG) {
Log.d(TAG, "Auto corrected by S-threshold.");
}
- return !shouldBlockAutoCorrectionBySafetyNet(consideredWord.toString(),
- suggestion.mWord);
+ return true;
}
}
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 ae415d0ab..d0613bd72 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -20,9 +20,7 @@ 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;
@@ -42,18 +40,17 @@ 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];
@@ -68,12 +65,14 @@ 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, final String dictType) {
- super(dictType);
+ 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;
mUseFullEditDistance = useFullEditDistance;
loadDictionary(filename, offset, length);
}
@@ -88,10 +87,8 @@ 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[] times, int[] pointerIds, int[] inputCodes, int codesSize,
- int commitPoint, boolean isGesture,
- int[] prevWordCodePointArray, boolean useFullEditDistance, char[] outputChars,
- int[] scores, int[] outputIndices);
+ int[] yCoordinates, int[] inputCodes, int codesSize, int[] prevWordForBigrams,
+ boolean useFullEditDistance, char[] outputChars, int[] scores);
private native int getBigramsNative(long dict, int[] prevWord, int prevWordLength,
int[] inputCodes, int inputCodesLength, char[] outputChars, int[] scores,
int maxWordLength, int maxBigrams);
@@ -106,9 +103,9 @@ public class BinaryDictionary extends Dictionary {
}
@Override
- public ArrayList<SuggestedWordInfo> getBigrams(final WordComposer codes,
- final CharSequence previousWord) {
- if (mNativeDict == 0) return null;
+ public void getBigrams(final WordComposer codes, final CharSequence previousWord,
+ final WordCallback callback) {
+ if (mNativeDict == 0) return;
int[] codePoints = StringUtils.toCodePointArray(previousWord.toString());
Arrays.fill(mOutputChars_bigrams, (char) 0);
@@ -126,7 +123,6 @@ 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;
@@ -135,22 +131,19 @@ public class BinaryDictionary extends Dictionary {
++len;
}
if (len > 0) {
- suggestions.add(new SuggestedWordInfo(
- new String(mOutputChars_bigrams, start, len),
- mBigramScores[j], SuggestedWordInfo.KIND_CORRECTION, mDictType));
+ callback.addWord(mOutputChars_bigrams, start, len, mBigramScores[j],
+ mDicTypeId, Dictionary.BIGRAM);
}
}
- return suggestions;
}
// proximityInfo and/or prevWordForBigrams may not be null.
@Override
- public ArrayList<SuggestedWordInfo> getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams,
+ final WordCallback callback, final ProximityInfo proximityInfo) {
final int count = getSuggestions(codes, prevWordForBigrams, proximityInfo, mOutputChars,
- mScores, mSpaceIndices);
+ mScores);
- 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;
@@ -159,13 +152,10 @@ public class BinaryDictionary extends Dictionary {
++len;
}
if (len > 0) {
- // TODO: actually get the kind from native code
- suggestions.add(new SuggestedWordInfo(
- new String(mOutputChars, start, len),
- mScores[j], SuggestedWordInfo.KIND_CORRECTION, mDictType));
+ callback.addWord(mOutputChars, start, len, mScores[j], mDicTypeId,
+ Dictionary.UNIGRAM);
}
}
- return suggestions;
}
/* package for test */ boolean isValidDictionary() {
@@ -175,7 +165,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, int[] spaceIndices) {
+ char[] outputChars, int[] scores) {
if (!isValidDictionary()) return -1;
final int codesSize = codes.size();
@@ -189,22 +179,14 @@ public class BinaryDictionary extends Dictionary {
Arrays.fill(outputChars, (char) 0);
Arrays.fill(scores, 0);
- // TODO: toLowerCase in the native code
- final int[] prevWordCodePointArray = (null == prevWordForBigrams)
+ final int[] prevWordCodePointArray = null == prevWordForBigrams
? null : StringUtils.toCodePointArray(prevWordForBigrams.toString());
- 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);
+ // TODO: pass the previous word to native code
+ return getSuggestionsNative(
+ mNativeDict, proximityInfo.getNativeProximityInfo(),
+ codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize,
+ prevWordCodePointArray, mUseFullEditDistance, outputChars, scores);
}
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
deleted file mode 100644
index cf977617d..000000000
--- a/java/src/com/android/inputmethod/latin/BoundedTreeSet.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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 5edc4314f..10e511eaf 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, Locale locale) {
- super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS);
+ public ContactsBinaryDictionary(final Context context, final int dicTypeId, Locale locale) {
+ super(context, getFilenameWithLocale(NAME, locale.toString()), dicTypeId);
mLocale = locale;
mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
registerObserver(context);
@@ -120,6 +120,12 @@ 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())) {
@@ -161,7 +167,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
* bigrams depending on locale.
*/
private void addName(String name) {
- int len = StringUtils.codePointCount(name);
+ int len = name.codePointCount(0, name.length());
String prevWord = null;
// TODO: Better tokenization for non-Latin writing systems
for (int i = 0; i < len; i++) {
@@ -171,7 +177,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
i = end - 1;
// Don't add single letter words, possibly confuses
// capitalization of i.
- final int wordLen = StringUtils.codePointCount(word);
+ final int wordLen = word.codePointCount(0, word.length());
if (wordLen < MAX_WORD_LENGTH && wordLen > 1) {
super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS);
if (!TextUtils.isEmpty(prevWord)) {
@@ -260,14 +266,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 = StringUtils.codePointCount(name);
+ int len = name.codePointCount(0, name.length());
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 = StringUtils.codePointCount(word);
+ final int wordLen = word.codePointCount(0, word.length());
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 0835450c1..9c3d46e70 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -17,9 +17,6 @@
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
@@ -31,43 +28,55 @@ public abstract class Dictionary {
*/
protected static final int FULL_WORD_SCORE_MULTIPLIER = 2;
- public static final int NOT_A_PROBABILITY = -1;
-
- 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 static final int UNIGRAM = 0;
+ public static final int BIGRAM = 1;
- public Dictionary(final String dictType) {
- mDictType = dictType;
+ 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);
}
/**
* Searches for words in the dictionary that match the characters in the composer. Matched
- * words are returned as an ArrayList.
- * @param composer the key sequence to match with coordinate info, as a WordComposer
+ * words are added through the callback object.
+ * @param composer the key sequence to match
* @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.
- * @return the list of suggestions
+ * @see WordCallback#addWord(char[], int, int, int, int, int)
*/
- abstract public ArrayList<SuggestedWordInfo> getWords(final WordComposer composer,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo);
+ abstract public void getWords(final WordComposer composer,
+ final CharSequence prevWordForBigrams, final WordCallback callback,
+ final ProximityInfo proximityInfo);
/**
- * Searches for pairs in the bigram dictionary that matches the previous word.
+ * Searches for pairs in the bigram dictionary that matches the previous word and all the
+ * possible words following are added through the callback object.
* @param composer the key sequence to match
* @param previousWord the word before
- * @return the list of suggestions
+ * @param callback the callback object to send possible word following previous word
*/
- public abstract ArrayList<SuggestedWordInfo> getBigrams(final WordComposer composer,
- final CharSequence previousWord);
+ public void getBigrams(final WordComposer composer, final CharSequence previousWord,
+ final WordCallback callback) {
+ // empty base implementation
+ }
/**
* 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 dcc53c59f..26c2e637e 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -17,11 +17,9 @@
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;
@@ -33,13 +31,11 @@ public class DictionaryCollection extends Dictionary {
private final String TAG = DictionaryCollection.class.getSimpleName();
protected final CopyOnWriteArrayList<Dictionary> mDictionaries;
- public DictionaryCollection(final String dictType) {
- super(dictType);
+ public DictionaryCollection() {
mDictionaries = new CopyOnWriteArrayList<Dictionary>();
}
- public DictionaryCollection(final String dictType, Dictionary... dictionaries) {
- super(dictType);
+ public DictionaryCollection(Dictionary... dictionaries) {
if (null == dictionaries) {
mDictionaries = new CopyOnWriteArrayList<Dictionary>();
} else {
@@ -48,48 +44,23 @@ public class DictionaryCollection extends Dictionary {
}
}
- public DictionaryCollection(final String dictType, Collection<Dictionary> dictionaries) {
- super(dictType);
+ public DictionaryCollection(Collection<Dictionary> dictionaries) {
mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
mDictionaries.removeAll(Collections.singleton(null));
}
@Override
- 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;
+ 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);
}
@Override
- 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;
+ public void getBigrams(final WordComposer composer, final CharSequence previousWord,
+ final WordCallback callback) {
+ for (final Dictionary dict : mDictionaries)
+ dict.getBigrams(composer, previousWord, callback);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
index 06a5f4b72..a22d73af7 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -49,8 +49,7 @@ public class DictionaryFactory {
final Locale locale, final boolean useFullEditDistance) {
if (null == locale) {
Log.e(TAG, "No locale defined for dictionary");
- return new DictionaryCollection(Dictionary.TYPE_MAIN,
- createBinaryDictionary(context, locale));
+ return new DictionaryCollection(createBinaryDictionary(context, locale));
}
final LinkedList<Dictionary> dictList = new LinkedList<Dictionary>();
@@ -60,7 +59,7 @@ public class DictionaryFactory {
for (final AssetFileAddress f : assetFileList) {
final BinaryDictionary binaryDictionary =
new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength,
- useFullEditDistance, locale, Dictionary.TYPE_MAIN);
+ useFullEditDistance, locale);
if (binaryDictionary.isValidDictionary()) {
dictList.add(binaryDictionary);
}
@@ -70,7 +69,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(Dictionary.TYPE_MAIN, dictList);
+ return new DictionaryCollection(dictList);
}
/**
@@ -113,7 +112,7 @@ public class DictionaryFactory {
return null;
}
return new BinaryDictionary(context, sourceDir, afd.getStartOffset(), afd.getLength(),
- false /* useFullEditDistance */, locale, Dictionary.TYPE_MAIN);
+ false /* useFullEditDistance */, locale);
} catch (android.content.res.Resources.NotFoundException e) {
Log.e(TAG, "Could not find the resource");
return null;
@@ -141,7 +140,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, Dictionary.TYPE_MAIN);
+ useFullEditDistance, locale);
} 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 1cda9f257..c65404cbc 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -19,7 +19,6 @@ 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;
@@ -76,6 +75,9 @@ 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
@@ -121,11 +123,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 dictionary type, as a human-readable string
+ * @param dictType The type of this dictionary.
*/
public ExpandableBinaryDictionary(
- final Context context, final String filename, final String dictType) {
- super(dictType);
+ final Context context, final String filename, final int dictType) {
+ mDicTypeId = dictType;
mFilename = filename;
mContext = context;
mBinaryDictionary = null;
@@ -192,47 +194,46 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
@Override
- public ArrayList<SuggestedWordInfo> getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams,
+ final WordCallback callback, final ProximityInfo proximityInfo) {
asyncReloadDictionaryIfRequired();
- return getWordsInner(codes, prevWordForBigrams, proximityInfo);
+ getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
}
- protected final ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ protected final void getWordsInner(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final WordCallback callback,
+ 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) {
- return mBinaryDictionary.getWords(codes, prevWordForBigrams, proximityInfo);
+ mBinaryDictionary.getWords(codes, prevWordForBigrams, callback, proximityInfo);
}
} finally {
mLocalDictionaryController.unlock();
}
}
- return null;
}
@Override
- public ArrayList<SuggestedWordInfo> getBigrams(final WordComposer codes,
- final CharSequence previousWord) {
+ public void getBigrams(final WordComposer codes, final CharSequence previousWord,
+ final WordCallback callback) {
asyncReloadDictionaryIfRequired();
- return getBigramsInner(codes, previousWord);
+ getBigramsInner(codes, previousWord, callback);
}
- protected ArrayList<SuggestedWordInfo> getBigramsInner(final WordComposer codes,
- final CharSequence previousWord) {
+ protected void getBigramsInner(final WordComposer codes, final CharSequence previousWord,
+ final WordCallback callback) {
if (mLocalDictionaryController.tryLock()) {
try {
if (mBinaryDictionary != null) {
- return mBinaryDictionary.getBigrams(codes, previousWord);
+ mBinaryDictionary.getBigrams(codes, previousWord, callback);
}
} finally {
mLocalDictionaryController.unlock();
}
}
- return null;
}
@Override
@@ -305,7 +306,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// Build the new binary dictionary
final BinaryDictionary newBinaryDictionary =
new BinaryDictionary(mContext, filename, 0, length, true /* useFullEditDistance */,
- null, mDictType);
+ null);
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 76213c0da..f5886aa12 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -38,6 +38,7 @@ 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;
@@ -151,11 +152,11 @@ public class ExpandableDictionary extends Dictionary {
private int[][] mCodes;
- public ExpandableDictionary(final Context context, final String dictType) {
- super(dictType);
+ public ExpandableDictionary(Context context, int dicTypeId) {
mContext = context;
clearDictionary();
mCodes = new int[BinaryDictionary.MAX_WORD_LENGTH][];
+ mDicTypeId = dicTypeId;
}
public void loadDictionary() {
@@ -247,20 +248,20 @@ public class ExpandableDictionary extends Dictionary {
}
@Override
- public ArrayList<SuggestedWordInfo> getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams,
+ final WordCallback callback, 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 null;
+ if (mUpdatingDictionary) return;
}
if (codes.size() >= BinaryDictionary.MAX_WORD_LENGTH) {
- return null;
+ return;
}
final ArrayList<SuggestedWordInfo> suggestions =
getWordsInner(codes, prevWordForBigrams, proximityInfo);
- return suggestions;
+ Utils.addAllSuggestions(mDicTypeId, Dictionary.UNIGRAM, suggestions, callback);
}
protected final ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes,
@@ -268,9 +269,8 @@ public class ExpandableDictionary extends Dictionary {
final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>();
mInputLength = codes.size();
if (mCodes.length < mInputLength) mCodes = new int[mInputLength][];
- final InputPointers ips = codes.getInputPointers();
- final int[] xCoordinates = ips.getXCoordinates();
- final int[] yCoordinates = ips.getYCoordinates();
+ final int[] xCoordinates = codes.getXCoordinates();
+ final int[] yCoordinates = codes.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, mDictType));
+ SuggestedWordInfo.KIND_CORRECTION));
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, mDictType));
+ finalFreq, SuggestedWordInfo.KIND_SHORTCUT));
if (suggestions.size() > Suggest.MAX_SUGGESTIONS) return false;
}
}
@@ -600,25 +600,22 @@ public class ExpandableDictionary extends Dictionary {
}
private void runBigramReverseLookUp(final CharSequence previousWord,
- final ArrayList<SuggestedWordInfo> suggestions) {
+ final WordCallback callback) {
// 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, suggestions);
+ reverseLookUp(prevWord.mNGrams, callback);
}
}
@Override
- public ArrayList<SuggestedWordInfo> getBigrams(final WordComposer codes,
- final CharSequence previousWord) {
+ public void getBigrams(final WordComposer codes, final CharSequence previousWord,
+ final WordCallback callback) {
if (!reloadDictionaryIfRequired()) {
- final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>();
- runBigramReverseLookUp(previousWord, suggestions);
- return suggestions;
+ runBigramReverseLookUp(previousWord, callback);
}
- return null;
}
/**
@@ -645,12 +642,11 @@ public class ExpandableDictionary extends Dictionary {
/**
* reverseLookUp retrieves the full word given a list of terminal nodes and adds those words
- * to the suggestions list passed as an argument.
+ * through callback.
* @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 ArrayList<SuggestedWordInfo> suggestions) {
+ final WordCallback callback) {
Node node;
int freq;
for (NextWord nextWord : terminalNodes) {
@@ -664,9 +660,8 @@ public class ExpandableDictionary extends Dictionary {
} while (node != null);
if (freq >= 0) {
- suggestions.add(new SuggestedWordInfo(new String(mLookedUpString, index,
- BinaryDictionary.MAX_WORD_LENGTH - index),
- freq, SuggestedWordInfo.KIND_CORRECTION, mDictType));
+ callback.addWord(mLookedUpString, index, BinaryDictionary.MAX_WORD_LENGTH - index,
+ freq, mDicTypeId, Dictionary.BIGRAM);
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
deleted file mode 100644
index 218243e9f..000000000
--- a/java/src/com/android/inputmethod/latin/InputPointers.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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 318aecb50..4e1f5fe92 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, "", "", NOT_A_SEPARATOR, null);
+ new LastComposedWord(null, 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 InputPointers inputPointers,
- final String typedWord, final String committedWord,
+ public LastComposedWord(final int[] primaryKeyCodes, final int[] xCoordinates,
+ final int[] yCoordinates, final String typedWord, final String committedWord,
final int separatorCode, final CharSequence prevWord) {
mPrimaryKeyCodes = primaryKeyCodes;
- if (inputPointers != null) {
- mInputPointers.copy(inputPointers);
- }
+ mXCoordinates = xCoordinates;
+ mYCoordinates = yCoordinates;
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 d4362f79e..8a5fc495e 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -470,7 +470,8 @@ 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, mPrefs);
+ mUserHistoryDictionary = UserHistoryDictionary.getInstance(
+ this, localeStr, Suggest.DIC_USER_HISTORY, mPrefs);
mSuggest.setUserHistoryDictionary(mUserHistoryDictionary);
}
@@ -498,7 +499,8 @@ 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, locale);
+ dictionaryToUse = new ContactsBinaryDictionary(
+ this, Suggest.DIC_CONTACTS, 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.
@@ -506,7 +508,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
dictionaryToUse = oldContactsDictionary;
}
} else {
- dictionaryToUse = new ContactsBinaryDictionary(this, locale);
+ dictionaryToUse = new ContactsBinaryDictionary(this, Suggest.DIC_CONTACTS, locale);
}
}
@@ -887,6 +889,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
applicationSuggestedWords,
false /* typedWordValid */,
false /* hasAutoCorrectionCandidate */,
+ false /* allowsToBeAutoCorrected */,
false /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */,
false /* isPrediction */);
@@ -1684,9 +1687,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
public void updateSuggestions() {
- mHandler.cancelUpdateSuggestions();
- mHandler.cancelUpdateBigramPredictions();
-
// Check if we have a suggestion engine attached.
if ((mSuggest == null || !mCurrentSettings.isSuggestionsRequested(mDisplayOrientation))) {
if (mWordComposer.isComposingWord()) {
@@ -1696,8 +1696,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return;
}
+ mHandler.cancelUpdateSuggestions();
+ mHandler.cancelUpdateBigramPredictions();
+
if (!mWordComposer.isComposingWord()) {
- // This is dead code: we can't come here with an empty word composer.
setPunctuationSuggestions();
return;
}
@@ -1708,7 +1710,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, false);
+ mCurrentSettings.mCorrectionEnabled);
// 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
@@ -1717,7 +1719,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.mTypedWordValid
+ || !suggestedWords.mAllowsToBeAutoCorrected
|| mSuggestionsView.isShowingAddToDictionaryHint()) {
showSuggestions(suggestedWords, typedWord);
} else {
@@ -1732,6 +1734,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
new SuggestedWords(typedWordAndPreviousSuggestions,
false /* typedWordValid */,
false /* hasAutoCorrectionCandidate */,
+ false /* allowsToBeAutoCorrected */,
false /* isPunctuationSuggestions */,
true /* isObsoleteSuggestions */,
false /* isPrediction */);
@@ -1742,7 +1745,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.mWillAutoCorrect) {
+ if (suggestedWords.hasAutoCorrectionWord()) {
autoCorrection = suggestedWords.getWord(1);
} else {
autoCorrection = typedWord;
@@ -1907,16 +1910,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
public void updateBigramPredictions() {
- 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());
- }
+ if (mSuggest == null || !mCurrentSettings.isSuggestionsRequested(mDisplayOrientation))
return;
- }
if (!mCurrentSettings.mBigramPredictionEnabled) {
setPunctuationSuggestions();
@@ -1927,9 +1922,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mCurrentSettings.mCorrectionEnabled) {
final CharSequence prevWord = mConnection.getThisWord(mCurrentSettings.mWordSeparators);
if (!TextUtils.isEmpty(prevWord)) {
- suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
- prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(),
- mCurrentSettings.mCorrectionEnabled, true);
+ suggestedWords = mSuggest.getBigramPredictions(prevWord);
} else {
suggestedWords = null;
}
diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
index e843848bc..dc0868e7c 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, String sourceDictionaryId) {
+ public static void onAddSuggestedWord(String word, int typeId, int dataType) {
}
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 e83d7c84a..cf3cc7873 100644
--- a/java/src/com/android/inputmethod/latin/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/latin/ResearchLogger.java
@@ -197,7 +197,6 @@ 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
@@ -380,52 +379,11 @@ 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) {
- // Current checks:
- // - Word not in dictionary
- // - Word contains numbers
- // - Privacy-safe word not logged recently
+ // currently: word not in dictionary or contains numbers.
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)) {
@@ -452,26 +410,15 @@ 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 flushEventQueue(
- boolean removePotentiallyPrivateEvents) {
+ /* package for test */ synchronized void flushQueue(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) {
@@ -532,9 +479,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
SuggestedWords words = (SuggestedWords) value;
mJsonWriter.beginObject();
mJsonWriter.name("typedWordValid").value(words.mTypedWordValid);
- mJsonWriter.name("willAutoCorrect").value(words.mWillAutoCorrect);
+ mJsonWriter.name("hasAutoCorrectionCandidate")
+ .value(words.mHasAutoCorrectionCandidate);
mJsonWriter.name("isPunctuationSuggestions")
.value(words.mIsPunctuationSuggestions);
+ mJsonWriter.name("allowsToBeAutoCorrected")
+ .value(words.mAllowsToBeAutoCorrected);
mJsonWriter.name("isObsoleteSuggestions")
.value(words.mIsObsoleteSuggestions);
mJsonWriter.name("isPrediction")
@@ -702,6 +652,7 @@ 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 = {
@@ -714,7 +665,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
};
final ResearchLogger researchLogger = getInstance();
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values);
- researchLogger.onWordComplete(scrubbedWord);
+ researchLogger.flushQueue(researchLogger.isPrivacyThreat(scrubbedWord));
}
private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = {
@@ -792,7 +743,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
final ResearchLogger researchLogger = getInstance();
researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values);
- researchLogger.flushEventQueue(true); // Play it safe. Remove privacy-sensitive events.
+ researchLogger.flushQueue(true); // Play it safe. Remove privacy-sensitive events.
}
}
@@ -873,6 +824,7 @@ 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 = {
@@ -887,6 +839,7 @@ 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 40d327ebb..0c19bed05 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -340,6 +340,13 @@ 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
@@ -356,15 +363,6 @@ 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 4c89a6e91..4c67b4957 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -143,39 +143,19 @@ 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 showKeyPreviewPopupOption = res.getBoolean(
+ final boolean showPopupOption = res.getBoolean(
R.bool.config_enable_show_popup_on_keypress_option);
- mKeyPreviewPopupDismissDelay =
- (ListPreference) findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
- if (!showKeyPreviewPopupOption) {
+ if (!showPopupOption) {
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 =
@@ -183,6 +163,23 @@ 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();
@@ -308,15 +305,13 @@ public class Settings extends InputMethodSettingsFragment
private void updateKeyPreviewPopupDelaySummary() {
final ListPreference lp = mKeyPreviewPopupDismissDelay;
- final CharSequence[] entries = lp.getEntries();
- if (entries == null || entries.length <= 0) return;
- lp.setSummary(entries[lp.findIndexOfValue(lp.getValue())]);
+ lp.setSummary(lp.getEntries()[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 aab84fccd..ef423f19b 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,
- Dictionary.TYPE_HARDCODED));
+ SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_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 6e7d985d6..a43b90525 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -184,9 +184,6 @@ 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 acc17ef3f..ca293060a 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
@@ -60,10 +60,6 @@ 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.
@@ -101,14 +97,6 @@ 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() {
@@ -205,14 +193,7 @@ public class SubtypeLocale {
}
public static String getKeyboardLayoutSetName(InputMethodSubtype subtype) {
- 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);
- }
+ final String keyboardLayoutSet = subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET);
// 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 dcfda86ea..892245402 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -18,6 +18,7 @@ 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;
@@ -25,7 +26,6 @@ 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,47 +34,80 @@ 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 {
+public class Suggest implements Dictionary.WordCallback {
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> mDictionaries =
+ private final ConcurrentHashMap<String, Dictionary> mUnigramDictionaries =
+ new ConcurrentHashMap<String, Dictionary>();
+ private final ConcurrentHashMap<String, Dictionary> mBigramDictionaries =
new ConcurrentHashMap<String, Dictionary>();
public static final int MAX_SUGGESTIONS = 18;
+ private static final int PREF_MAX_BIGRAMS = 60;
+
private float mAutoCorrectionThreshold;
- // Locale used for upper- and title-casing words
- final private Locale mLocale;
+ 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;
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(mDictionaries, Dictionary.TYPE_MAIN, mainDict);
+ addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict);
+ addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict);
initWhitelistAndAutocorrectAndPool(context, locale);
}
private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) {
mWhiteListDictionary = new WhitelistDictionary(context, locale);
- addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_WHITELIST, mWhiteListDictionary);
+ addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary);
}
private void initAsynchronously(final Context context, final Locale locale) {
@@ -103,7 +136,8 @@ public class Suggest {
public void run() {
final DictionaryCollection newMainDict =
DictionaryFactory.createMainDictionaryFromManager(context, locale);
- addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, newMainDict);
+ addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, newMainDict);
+ addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, newMainDict);
mMainDictionary = newMainDict;
}
}.start();
@@ -124,7 +158,11 @@ public class Suggest {
}
public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() {
- return mDictionaries;
+ return mUnigramDictionaries;
+ }
+
+ public static int getApproxMaxWordLength() {
+ return APPROX_MAX_WORD_LENGTH;
}
/**
@@ -132,7 +170,7 @@ public class Suggest {
* before the main dictionary, if set. This refers to the system-managed user dictionary.
*/
public void setUserDictionary(UserBinaryDictionary userDictionary) {
- addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER, userDictionary);
+ addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER, userDictionary);
}
/**
@@ -142,157 +180,235 @@ public class Suggest {
*/
public void setContactsDictionary(ContactsBinaryDictionary contactsDictionary) {
mContactsDict = contactsDictionary;
- addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_CONTACTS, contactsDictionary);
+ addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
+ addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
}
public void setUserHistoryDictionary(UserHistoryDictionary userHistoryDictionary) {
- addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER_HISTORY, userHistoryDictionary);
+ addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER_HISTORY_UNIGRAM,
+ userHistoryDictionary);
+ addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_USER_HISTORY_BIGRAM,
+ 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,
- // TODO: remove isPrediction parameter. It effectively means the same thing
- // as wordComposer.size() <= 1
- final boolean isPrediction) {
+ final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) {
LatinImeLogger.onStartSuggestion(prevWordForBigram);
- 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);
+ mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
+ mIsAllUpperCase = wordComposer.isAllUpperCase();
+ mTrailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
+ mSuggestions = new ArrayList<SuggestedWordInfo>(MAX_SUGGESTIONS);
final String typedWord = wordComposer.getTypedWord();
- final String consideredWord = trailingSingleQuotesCount > 0
- ? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount)
+ final String consideredWord = mTrailingSingleQuotesCount > 0
+ ? typedWord.substring(0, typedWord.length() - mTrailingSingleQuotesCount)
: typedWord;
- LatinImeLogger.onAddSuggestedWord(typedWord, Dictionary.TYPE_USER_TYPED);
+ // Treating USER_TYPED as UNIGRAM suggestion for logging now.
+ LatinImeLogger.onAddSuggestedWord(typedWord, Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM);
+ mConsideredWord = consideredWord;
if (wordComposer.size() <= 1 && isCorrectionEnabled) {
// At first character typed, search only the bigrams
+ mBigramSuggestions = new ArrayList<SuggestedWordInfo>(PREF_MAX_BIGRAMS);
+
if (!TextUtils.isEmpty(prevWordForBigram)) {
- final CharSequence lowerPrevWord;
- if (StringUtils.hasUpperCase(prevWordForBigram)) {
- // TODO: Must pay attention to locale when changing case.
- lowerPrevWord = prevWordForBigram.toString().toLowerCase();
+ getAllBigrams(prevWordForBigram, wordComposer);
+ if (TextUtils.isEmpty(consideredWord)) {
+ // 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));
+ }
} else {
- lowerPrevWord = null;
- }
- for (final String key : mDictionaries.keySet()) {
- final Dictionary dictionary = mDictionaries.get(key);
- suggestionsSet.addAll(dictionary.getBigrams(wordComposer, prevWordForBigram));
- if (null != lowerPrevWord) {
- suggestionsSet.addAll(dictionary.getBigrams(wordComposer, lowerPrevWord));
+ // Word entered: return only bigrams that match the first char of the typed word
+ final char currentChar = consideredWord.charAt(0);
+ // TODO: Must pay attention to locale when changing case.
+ // TODO: Use codepoint instead of char
+ final char currentCharUpper = Character.toUpperCase(currentChar);
+ int count = 0;
+ final int bigramSuggestionSize = mBigramSuggestions.size();
+ for (int i = 0; i < bigramSuggestionSize; i++) {
+ final SuggestedWordInfo bigramSuggestion = mBigramSuggestions.get(i);
+ final char bigramSuggestionFirstChar =
+ (char)bigramSuggestion.codePointAt(0);
+ if (bigramSuggestionFirstChar == currentChar
+ || bigramSuggestionFirstChar == currentCharUpper) {
+ addBigramToSuggestions(bigramSuggestion);
+ if (++count > MAX_SUGGESTIONS) break;
+ }
}
}
}
+
} else if (wordComposer.size() > 1) {
final WordComposer wordComposerForLookup;
- if (trailingSingleQuotesCount > 0) {
+ if (mTrailingSingleQuotesCount > 0) {
wordComposerForLookup = new WordComposer(wordComposer);
- for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
+ for (int i = mTrailingSingleQuotesCount - 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 : mDictionaries.keySet()) {
+ for (final String key : mUnigramDictionaries.keySet()) {
// Skip UserUnigramDictionary and WhitelistDictionary to lookup
- if (key.equals(Dictionary.TYPE_USER_HISTORY)
- || key.equals(Dictionary.TYPE_WHITELIST))
+ if (key.equals(DICT_KEY_USER_HISTORY_UNIGRAM) || key.equals(DICT_KEY_WHITELIST))
continue;
- final Dictionary dictionary = mDictionaries.get(key);
- suggestionsSet.addAll(dictionary.getWords(
- wordComposerForLookup, prevWordForBigram, proximityInfo));
+ final Dictionary dictionary = mUnigramDictionaries.get(key);
+ dictionary.getWords(wordComposerForLookup, prevWordForBigram, this, proximityInfo);
}
}
- // 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 CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase,
+ mIsFirstCharCapitalized, mWhiteListDictionary.getWhitelistedWord(consideredWord));
final boolean hasAutoCorrection;
- 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;
+ if (isCorrectionEnabled) {
+ final CharSequence autoCorrection =
+ AutoCorrection.computeAutoCorrectionWord(mUnigramDictionaries, wordComposer,
+ mSuggestions, consideredWord, mAutoCorrectionThreshold,
+ whitelistedWord);
+ hasAutoCorrection = (null != autoCorrection);
} else {
hasAutoCorrection = false;
}
if (whitelistedWord != null) {
- 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);
+ 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));
}
}
- 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);
+ mSuggestions.add(0, new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE,
+ SuggestedWordInfo.KIND_TYPED));
+ SuggestedWordInfo.removeDups(mSuggestions);
final ArrayList<SuggestedWordInfo> suggestionsList;
- if (DBG && !suggestionsContainer.isEmpty()) {
- suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, suggestionsContainer);
+ if (DBG) {
+ suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, mSuggestions);
} else {
- suggestionsList = suggestionsContainer;
+ suggestionsList = mSuggestions;
}
+ // 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,
- // 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 */
+ !allowsToBeAutoCorrected /* typedWordValid */,
+ autoCorrectionAvailable /* hasAutoCorrectionCandidate */,
+ allowsToBeAutoCorrected /* allowsToBeAutoCorrected */,
false /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */,
- isPrediction);
+ 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);
+ }
}
private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo(
@@ -321,44 +437,120 @@ public class Suggest {
return suggestionsList;
}
- 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());
+ // 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 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));
+
+ 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);
+ }
} else {
- sb.append(wordInfo.mWord);
+ sb.append(word, offset, length);
}
- for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
+ for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
}
- return new SuggestedWordInfo(sb, wordInfo.mScore, wordInfo.mKind, wordInfo.mSourceDict);
+ // 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;
}
public void close() {
final HashSet<Dictionary> dictionaries = new HashSet<Dictionary>();
- dictionaries.addAll(mDictionaries.values());
+ dictionaries.addAll(mUnigramDictionaries.values());
+ dictionaries.addAll(mBigramDictionaries.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 94af301a2..45ac9ff53 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);
+ new ArrayList<SuggestedWordInfo>(0), false, false, false, false, false, false);
public final boolean mTypedWordValid;
- // 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 mHasAutoCorrectionCandidate;
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 willAutoCorrect,
+ final boolean hasAutoCorrectionCandidate,
+ final boolean allowsToBeAutoCorrected,
final boolean isPunctuationSuggestions,
final boolean isObsoleteSuggestions,
final boolean isPrediction) {
mSuggestedWordInfoList = suggestedWordInfoList;
mTypedWordValid = typedWordValid;
- mWillAutoCorrect = willAutoCorrect;
+ mHasAutoCorrectionCandidate = hasAutoCorrectionCandidate;
+ mAllowsToBeAutoCorrected = allowsToBeAutoCorrected;
mIsPunctuationSuggestions = isPunctuationSuggestions;
mIsObsoleteSuggestions = isObsoleteSuggestions;
mIsPrediction = isPrediction;
@@ -67,8 +67,12 @@ public class SuggestedWords {
return mSuggestedWordInfoList.get(pos);
}
+ public boolean hasAutoCorrectionWord() {
+ return mHasAutoCorrectionCandidate && size() > 1 && !mTypedWordValid;
+ }
+
public boolean willAutoCorrect() {
- return mWillAutoCorrect;
+ return !mTypedWordValid && mHasAutoCorrectionCandidate;
}
@Override
@@ -76,7 +80,8 @@ public class SuggestedWords {
// Pretty-print method to help debug
return "SuggestedWords:"
+ " mTypedWordValid=" + mTypedWordValid
- + " mWillAutoCorrect=" + mWillAutoCorrect
+ + " mHasAutoCorrectionCandidate=" + mHasAutoCorrectionCandidate
+ + " mAllowsToBeAutoCorrected=" + mAllowsToBeAutoCorrected
+ " mIsPunctuationSuggestions=" + mIsPunctuationSuggestions
+ " words=" + Arrays.toString(mSuggestedWordInfoList.toArray());
}
@@ -87,7 +92,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, Dictionary.TYPE_APPLICATION_DEFINED));
+ SuggestedWordInfo.KIND_APP_DEFINED));
}
}
return result;
@@ -100,7 +105,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, Dictionary.TYPE_USER_TYPED));
+ SuggestedWordInfo.KIND_TYPED));
alreadySeen.add(typedWord.toString());
final int previousSize = previousSuggestions.size();
for (int pos = 1; pos < previousSize; pos++) {
@@ -130,17 +135,14 @@ public class SuggestedWords {
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,
- final String sourceDict) {
+ public SuggestedWordInfo(final CharSequence word, final int score, final int kind) {
mWordStr = word.toString();
mWord = word;
mScore = score;
mKind = kind;
- mSourceDict = sourceDict;
- mCodePointCount = StringUtils.codePointCount(mWordStr);
+ mCodePointCount = mWordStr.codePointCount(0, mWordStr.length());
}
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
index 9b20bd690..673b54500 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
@@ -19,23 +19,22 @@ 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, locale);
+ super(context, Suggest.DIC_CONTACTS, locale);
}
@Override
- public synchronized ArrayList<SuggestedWordInfo> getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ public synchronized void getWords(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final WordCallback callback,
+ final ProximityInfo proximityInfo) {
syncReloadDictionaryIfRequired();
- return getWordsInner(codes, prevWordForBigrams, proximityInfo);
+ getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
index 5b2a6edec..1606a34e0 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
@@ -19,9 +19,6 @@ 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 {
@@ -35,10 +32,11 @@ public class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDictionar
}
@Override
- public synchronized ArrayList<SuggestedWordInfo> getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ public synchronized void getWords(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final WordCallback callback,
+ final ProximityInfo proximityInfo) {
syncReloadDictionaryIfRequired();
- return getWordsInner(codes, prevWordForBigrams, proximityInfo);
+ getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index 60e6fa127..5bcdb57b5 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -34,10 +34,7 @@ import java.util.Arrays;
*/
public class UserBinaryDictionary extends ExpandableBinaryDictionary {
- // 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
+ // TODO: use Words.SHORTCUT when it's public in the SDK
final static String SHORTCUT = "shortcut";
private static final String[] PROJECTION_QUERY;
static {
@@ -72,14 +69,9 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
public UserBinaryDictionary(final Context context, final String locale,
final boolean alsoUseMoreRestrictiveLocales) {
- super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_USER);
+ super(context, getFilenameWithLocale(NAME, locale), Suggest.DIC_USER);
if (null == locale) throw new NullPointerException(); // Catch the error earlier
- 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;
- }
+ 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 73fa83f9a..5095f6582 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
@@ -115,7 +115,8 @@ public class UserHistoryDictionary extends ExpandableDictionary {
}
public synchronized static UserHistoryDictionary getInstance(
- final Context context, final String locale, final SharedPreferences sp) {
+ final Context context, final String locale,
+ final int dictTypeId, final SharedPreferences sp) {
if (sLangDictCache.containsKey(locale)) {
final SoftReference<UserHistoryDictionary> ref = sLangDictCache.get(locale);
final UserHistoryDictionary dict = ref == null ? null : ref.get();
@@ -127,14 +128,14 @@ public class UserHistoryDictionary extends ExpandableDictionary {
}
}
final UserHistoryDictionary dict =
- new UserHistoryDictionary(context, locale, sp);
+ new UserHistoryDictionary(context, locale, dictTypeId, sp);
sLangDictCache.put(locale, new SoftReference<UserHistoryDictionary>(dict));
return dict;
}
- private UserHistoryDictionary(final Context context, final String locale,
- final SharedPreferences sp) {
- super(context, Dictionary.TYPE_USER_HISTORY);
+ private UserHistoryDictionary(final Context context, final String locale, final int dicTypeId,
+ SharedPreferences sp) {
+ super(context, dicTypeId);
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 8f71de0e7..a44b1f9ad 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -531,4 +531,14 @@ 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 3af22140e..a0de2f970 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, Dictionary.TYPE_WHITELIST);
+ super(context, Suggest.DIC_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 98282f970..ca9caa1d3 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -34,7 +34,8 @@ public class WordComposer {
private static final int N = BinaryDictionary.MAX_WORD_LENGTH;
private int[] mPrimaryKeyCodes;
- private final InputPointers mInputPointers = new InputPointers();
+ private int[] mXCoordinates;
+ private int[] mYCoordinates;
private StringBuilder mTypedWord;
private CharSequence mAutoCorrection;
private boolean mIsResumed;
@@ -53,6 +54,8 @@ 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;
@@ -66,7 +69,8 @@ public class WordComposer {
public void init(WordComposer source) {
mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length);
mTypedWord = new StringBuilder(source.mTypedWord);
- mInputPointers.copy(source.mInputPointers);
+ mXCoordinates = Arrays.copyOf(source.mXCoordinates, source.mXCoordinates.length);
+ mYCoordinates = Arrays.copyOf(source.mYCoordinates, source.mYCoordinates.length);
mCapsCount = source.mCapsCount;
mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
mAutoCapitalized = source.mAutoCapitalized;
@@ -88,7 +92,7 @@ public class WordComposer {
refreshSize();
}
- private final void refreshSize() {
+ public final void refreshSize() {
mCodePointSize = mTypedWord.codePointCount(0, mTypedWord.length());
}
@@ -112,8 +116,12 @@ public class WordComposer {
return mPrimaryKeyCodes[index];
}
- public InputPointers getInputPointers() {
- return mInputPointers;
+ public int[] getXCoordinates() {
+ return mXCoordinates;
+ }
+
+ public int[] getYCoordinates() {
+ return mYCoordinates;
}
private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) {
@@ -149,8 +157,8 @@ public class WordComposer {
if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) {
mPrimaryKeyCodes[newIndex] = primaryCode >= Keyboard.CODE_SPACE
? Character.toLowerCase(primaryCode) : primaryCode;
- // TODO: Set correct pointer id and time
- mInputPointers.addPointer(newIndex, keyX, keyY, 0, 0);
+ mXCoordinates[newIndex] = keyX;
+ mYCoordinates[newIndex] = keyY;
}
mIsFirstCharCapitalized = isFirstCharCapitalized(
newIndex, primaryCode, mIsFirstCharCapitalized);
@@ -310,11 +318,14 @@ 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,
- mInputPointers, mTypedWord.toString(), committedWord, separatorCode,
+ xCoordinates, yCoordinates, mTypedWord.toString(), committedWord, separatorCode,
prevWord);
- mInputPointers.reset();
if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD
&& type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) {
lastComposedWord.deactivate();
@@ -328,7 +339,8 @@ public class WordComposer {
public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes;
- mInputPointers.set(lastComposedWord.mInputPointers);
+ mXCoordinates = lastComposedWord.mXCoordinates;
+ mYCoordinates = lastComposedWord.mYCoordinates;
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 5f4d66091..7fffc31c0 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,8 +203,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
EMPTY_STRING_ARRAY);
}
- // TODO: remove this class and replace it by storage local to the session.
- private static class SuggestionsGatherer {
+ private static class SuggestionsGatherer implements WordCallback {
public static class Result {
public final String[] mSuggestions;
public final boolean mHasRecommendedSuggestions;
@@ -238,8 +237,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService
mScores = new int[mMaxLength];
}
- synchronized public boolean addWord(char[] word, int[] spaceIndices, int wordOffset,
- int wordLength, int score) {
+ @Override
+ synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score,
+ int dicTypeId, int dataType) {
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,13 +780,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService
try {
dictInfo = mDictionaryPool.takeOrGetNull();
if (null == dictInfo) return getNotInDictEmptySuggestions();
- 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);
- }
+ dictInfo.mDictionary.getWords(composer, prevWord, suggestionsGatherer,
+ dictInfo.mProximityInfo);
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 642a551ce..e86390b11 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
- && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet(
+ if (index == mCenterSuggestionIndex && suggestedWords.mHasAutoCorrectionCandidate
+ && Suggest.shouldBlockAutoCorrectionBySafetyNet(
suggestedWords.getWord(1).toString(), suggestedWords.getWord(0))) {
return 0xFFFF0000;
}