aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java5
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java9
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java54
-rw-r--r--java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java2
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java50
-rw-r--r--java/src/com/android/inputmethod/latin/Constants.java1
-rw-r--r--java/src/com/android/inputmethod/latin/InputPointers.java11
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java40
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java9
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java20
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java11
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java9
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java76
13 files changed, 256 insertions, 41 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java
index 702ed2075..546fa8140 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java
@@ -505,10 +505,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
@Override
public void onKeyClick(final Key key) {
- // TODO: Save emoticons to recents
- if (mEmojiCategory.getCurrentCategoryId() != CATEGORY_ID_EMOTICONS) {
- mEmojiKeyboardAdapter.addRecentKey(key);
- }
+ mEmojiKeyboardAdapter.addRecentKey(key);
mEmojiCategory.saveLastTypedCategoryPage();
final int code = key.getCode();
if (code == Constants.CODE_OUTPUT_TEXT) {
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 23f037fbd..bc1383aff 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -155,6 +155,15 @@ public class Keyboard {
return mKeys;
}
+ public Key getKeyFromOutputText(final String outputText) {
+ for (final Key key : getKeys()) {
+ if (outputText.equals(key.getOutputText())) {
+ return key;
+ }
+ }
+ return null;
+ }
+
public Key getKey(final int code) {
if (code == Constants.CODE_UNSPECIFIED) {
return null;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
index f203eb7d7..2976e2323 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
@@ -18,25 +18,27 @@ package com.android.inputmethod.keyboard.internal;
import android.content.SharedPreferences;
import android.text.TextUtils;
+import android.util.Log;
import com.android.inputmethod.keyboard.EmojiKeyboardView;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.StringUtils;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
/**
* This is a Keyboard class where you can add keys dynamically shown in a grid layout
*/
public class DynamicGridKeyboard extends Keyboard {
+ private static final String TAG = DynamicGridKeyboard.class.getSimpleName();
private static final int TEMPLATE_KEY_CODE_0 = 0x30;
private static final int TEMPLATE_KEY_CODE_1 = 0x31;
- // Recent codes are saved as an integer array, so we use comma as a separater.
- private static final String RECENT_KEY_SEPARATOR = Constants.STRING_COMMA;
private final SharedPreferences mPrefs;
private final int mLeftPadding;
@@ -84,6 +86,9 @@ public class DynamicGridKeyboard extends Keyboard {
}
private void addKey(final Key usedKey, final boolean addFirst) {
+ if (usedKey == null) {
+ return;
+ }
synchronized (mGridKeys) {
mCachedGridKeys = null;
final GridKey key = new GridKey(usedKey);
@@ -109,28 +114,45 @@ public class DynamicGridKeyboard extends Keyboard {
}
private void saveRecentKeys() {
- final StringBuilder sb = new StringBuilder();
+ final ArrayList<Object> keys = CollectionUtils.newArrayList();
for (final Key key : mGridKeys) {
- sb.append(key.getCode()).append(RECENT_KEY_SEPARATOR);
+ if (key.getOutputText() != null) {
+ keys.add(key.getOutputText());
+ } else {
+ keys.add(key.getCode());
+ }
}
- Settings.writeEmojiRecentKeys(mPrefs, sb.toString());
+ final String jsonStr = StringUtils.listToJsonStr(keys);
+ Settings.writeEmojiRecentKeys(mPrefs, jsonStr);
}
- public void loadRecentKeys(Collection<DynamicGridKeyboard> keyboards) {
- final String str = Settings.readEmojiRecentKeys(mPrefs);
- for (String s : str.split(RECENT_KEY_SEPARATOR)) {
- if (TextUtils.isEmpty(s)) {
- continue;
- }
- final int code = Integer.valueOf(s);
- for (DynamicGridKeyboard kbd : keyboards) {
+ private static Key getKey(final Collection<DynamicGridKeyboard> keyboards, final Object o) {
+ for (final DynamicGridKeyboard kbd : keyboards) {
+ if (o instanceof Integer) {
+ final int code = (Integer) o;
final Key key = kbd.getKey(code);
if (key != null) {
- addKeyLast(key);
- break;
+ return key;
+ }
+ } else if (o instanceof String) {
+ final String outputText = (String) o;
+ final Key key = kbd.getKeyFromOutputText(outputText);
+ if (key != null) {
+ return key;
}
+ } else {
+ Log.w(TAG, "Invalid object: " + o);
}
}
+ return null;
+ }
+
+ public void loadRecentKeys(Collection<DynamicGridKeyboard> keyboards) {
+ final String str = Settings.readEmojiRecentKeys(mPrefs);
+ final List<Object> keys = StringUtils.jsonStrToList(str);
+ for (final Object o : keys) {
+ addKeyLast(getKey(keyboards, o));
+ }
}
private int getKeyX(final int index) {
diff --git a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
index 55df263fe..845a9b987 100644
--- a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
@@ -58,7 +58,7 @@ abstract public class AbstractDictionaryWriter extends Dictionary {
final File file = new File(mContext.getFilesDir(), fileName);
final File tempFile = new File(mContext.getFilesDir(), tempFileName);
try {
- final DictEncoder dictEncoder = new Ver3DictEncoder(file);
+ final DictEncoder dictEncoder = new Ver3DictEncoder(tempFile);
writeDictionary(dictEncoder);
tempFile.renameTo(file);
} catch (IOException e) {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index dacb8483c..632ee0da4 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.latin;
import android.text.TextUtils;
import android.util.SparseArray;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.settings.NativeSuggestOptions;
@@ -47,12 +48,13 @@ public final class BinaryDictionary extends Dictionary {
private long mNativeDict;
private final Locale mLocale;
private final long mDictSize;
+ private final String mDictFilePath;
private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH];
private final int[] mOutputCodePoints = new int[MAX_WORD_LENGTH * MAX_RESULTS];
private final int[] mSpaceIndices = new int[MAX_RESULTS];
private final int[] mOutputScores = new int[MAX_RESULTS];
private final int[] mOutputTypes = new int[MAX_RESULTS];
- private final int[] mOutputAutoCommitFirstWordConfidence = new int[1]; // Only one result
+ private final int[] mOutputAutoCommitFirstWordConfidence = new int[MAX_RESULTS];
private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions();
@@ -91,6 +93,7 @@ public final class BinaryDictionary extends Dictionary {
super(dictType);
mLocale = locale;
mDictSize = length;
+ mDictFilePath = filename;
mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance);
loadDictionary(filename, offset, length, isUpdatable);
}
@@ -101,9 +104,12 @@ public final class BinaryDictionary extends Dictionary {
private static native long openNative(String sourceDir, long dictOffset, long dictSize,
boolean isUpdatable);
+ private static native void flushNative(long dict, String filePath);
+ private static native boolean needsToRunGCNative(long dict);
+ private static native void flushWithGCNative(long dict, String filePath);
private static native void closeNative(long dict);
private static native int getProbabilityNative(long dict, int[] word);
- private static native boolean isValidBigramNative(long dict, int[] word0, int[] word1);
+ private static native int getBigramProbabilityNative(long dict, int[] word0, int[] word1);
private static native int getSuggestionsNative(long dict, long proximityInfo,
long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times,
int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint,
@@ -116,6 +122,8 @@ public final class BinaryDictionary extends Dictionary {
private static native void addBigramWordsNative(long dict, int[] word0, int[] word1,
int probability);
private static native void removeBigramWordsNative(long dict, int[] word0, int[] word1);
+ private static native int calculateProbabilityNative(long dict, int unigramProbability,
+ int bigramProbability);
// TODO: Move native dict into session
private final void loadDictionary(final String path, final long startOffset,
@@ -186,7 +194,7 @@ public final class BinaryDictionary extends Dictionary {
// flags too and pass mOutputTypes[j] instead of kind
suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len),
score, kind, this /* sourceDict */,
- mSpaceIndices[0] /* indexOfTouchPointOfSecondWord */,
+ mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */,
mOutputAutoCommitFirstWordConfidence[0]));
}
}
@@ -213,12 +221,12 @@ public final class BinaryDictionary extends Dictionary {
@Override
public boolean isValidWord(final String word) {
- return getFrequency(word) >= 0;
+ return getFrequency(word) != NOT_A_PROBABILITY;
}
@Override
public int getFrequency(final String word) {
- if (word == null) return -1;
+ if (word == null) return NOT_A_PROBABILITY;
int[] codePoints = StringUtils.toCodePointArray(word);
return getProbabilityNative(mNativeDict, codePoints);
}
@@ -226,10 +234,14 @@ public final class BinaryDictionary extends Dictionary {
// TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni
// calls when checking for changes in an entire dictionary.
public boolean isValidBigram(final String word0, final String word1) {
- if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) return false;
+ return getBigramProbability(word0, word1) != NOT_A_PROBABILITY;
+ }
+
+ public int getBigramProbability(final String word0, final String word1) {
+ if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) return NOT_A_PROBABILITY;
final int[] codePoints0 = StringUtils.toCodePointArray(word0);
final int[] codePoints1 = StringUtils.toCodePointArray(word1);
- return isValidBigramNative(mNativeDict, codePoints0, codePoints1);
+ return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1);
}
// Add a unigram entry to binary dictionary in native code.
@@ -261,6 +273,30 @@ public final class BinaryDictionary extends Dictionary {
removeBigramWordsNative(mNativeDict, codePoints0, codePoints1);
}
+ @UsedForTesting
+ public void flush() {
+ if (!isValidDictionary()) return;
+ flushNative(mNativeDict, mDictFilePath);
+ }
+
+ @UsedForTesting
+ public void flushWithGC() {
+ if (!isValidDictionary()) return;
+ flushWithGCNative(mNativeDict, mDictFilePath);
+ }
+
+ @UsedForTesting
+ public boolean needsToRunGC() {
+ if (!isValidDictionary()) return false;
+ return needsToRunGCNative(mNativeDict);
+ }
+
+ @UsedForTesting
+ public int calculateProbability(final int unigramProbability, final int bigramProbability) {
+ if (!isValidDictionary()) return NOT_A_PROBABILITY;
+ return calculateProbabilityNative(mNativeDict, unigramProbability, bigramProbability);
+ }
+
@Override
public boolean shouldAutoCommit(final SuggestedWordInfo candidate) {
// TODO: actually use the confidence rather than use this completely broken heuristic
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index 6976f5f3a..58cae7dd5 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -226,7 +226,6 @@ public final class Constants {
}
public static final int MAX_INT_BIT_COUNT = 32;
- public static final String STRING_COMMA = ",";
private Constants() {
// This utility class is not publicly instantiable.
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
index e96a46e12..2e638aaf3 100644
--- a/java/src/com/android/inputmethod/latin/InputPointers.java
+++ b/java/src/com/android/inputmethod/latin/InputPointers.java
@@ -105,6 +105,17 @@ public final class InputPointers {
mTimes.append(times, startPos, length);
}
+ /**
+ * Shift to the left by elementCount, discarding elementCount pointers at the start.
+ * @param elementCount how many elements to shift.
+ */
+ public void shift(final int elementCount) {
+ mXCoordinates.shift(elementCount);
+ mYCoordinates.shift(elementCount);
+ mPointerIds.shift(elementCount);
+ mTimes.shift(elementCount);
+ }
+
public void reset() {
final int defaultCapacity = mDefaultCapacity;
mXCoordinates.reset(defaultCapacity);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index d8a47a307..bfb904422 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1847,10 +1847,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void onUpdateBatchInput(final InputPointers batchPointers) {
- final SuggestedWordInfo candidate = mSuggestedWords.getAutoCommitCandidate();
- if (null != candidate) {
- if (candidate.mSourceDict.shouldAutoCommit(candidate)) {
- // TODO: implement auto-commit
+ if (mSettings.getCurrent().mPhraseGestureEnabled) {
+ final SuggestedWordInfo candidate = mSuggestedWords.getAutoCommitCandidate();
+ if (null != candidate) {
+ if (candidate.mSourceDict.shouldAutoCommit(candidate)) {
+ final String[] commitParts = candidate.mWord.split(" ", 2);
+ batchPointers.shift(candidate.mIndexOfTouchPointOfSecondWord);
+ promotePhantomSpace();
+ mConnection.commitText(commitParts[0], 0);
+ mSpaceState = SPACE_STATE_PHANTOM;
+ mKeyboardSwitcher.updateShiftState();
+ mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
+ }
}
}
mInputUpdater.onUpdateBatchInput(batchPointers);
@@ -1863,12 +1871,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (TextUtils.isEmpty(batchInputText)) {
return;
}
- mWordComposer.setBatchInputWord(batchInputText);
mConnection.beginBatchEdit();
if (SPACE_STATE_PHANTOM == mSpaceState) {
promotePhantomSpace();
}
- mConnection.setComposingText(batchInputText, 1);
+ if (mSettings.getCurrent().mPhraseGestureEnabled) {
+ // Find the last space
+ final int indexOfLastSpace = batchInputText.lastIndexOf(Constants.CODE_SPACE) + 1;
+ if (0 != indexOfLastSpace) {
+ mConnection.commitText(batchInputText.substring(0, indexOfLastSpace), 1);
+ showSuggestionStrip(suggestedWords.getSuggestedWordsForLastWordOfPhraseGesture());
+ }
+ final String lastWord = batchInputText.substring(indexOfLastSpace);
+ mWordComposer.setBatchInputWord(lastWord);
+ mConnection.setComposingText(lastWord, 1);
+ } else {
+ mWordComposer.setBatchInputWord(batchInputText);
+ mConnection.setComposingText(batchInputText, 1);
+ }
mExpectingUpdateSelection = true;
mConnection.endBatchEdit();
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
@@ -2665,6 +2685,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return prevWord;
}
+ private boolean isResumableWord(final String word, final SettingsValues settings) {
+ final int firstCodePoint = word.codePointAt(0);
+ return settings.isWordCodePoint(firstCodePoint)
+ && Constants.CODE_SINGLE_QUOTE != firstCodePoint
+ && Constants.CODE_DASH != firstCodePoint;
+ }
+
/**
* Check if the cursor is touching a word. If so, restart suggestions on this word, else
* do nothing.
@@ -2694,6 +2721,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (numberOfCharsInWordBeforeCursor > mLastSelectionStart) return;
final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
final String typedWord = range.mWord.toString();
+ if (!isResumableWord(typedWord, currentSettings)) return;
int i = 0;
for (final SuggestionSpan span : range.getSuggestionSpansAtWord()) {
for (final String s : span.getSuggestions()) {
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 7815f4d41..1684d47b5 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -460,7 +460,7 @@ public final class Suggest {
private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator =
new SuggestedWordInfoComparator();
- private static SuggestedWordInfo getTransformedSuggestedWordInfo(
+ /* package for test */ 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());
@@ -471,7 +471,12 @@ public final class Suggest {
} else {
sb.append(wordInfo.mWord);
}
- for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
+ // Appending quotes is here to help people quote words. However, it's not helpful
+ // when they type words with quotes toward the end like "it's" or "didn't", where
+ // it's more likely the user missed the last character (or didn't type it yet).
+ final int quotesToAppend = trailingSingleQuotesCount
+ - (-1 == wordInfo.mWord.indexOf(Constants.CODE_SINGLE_QUOTE) ? 0 : 1);
+ for (int i = quotesToAppend - 1; i >= 0; --i) {
sb.appendCodePoint(Constants.CODE_SINGLE_QUOTE);
}
return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKind,
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 17637054a..fed4cdbbb 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -276,4 +276,24 @@ public final class SuggestedWords {
false /* willAutoCorrect */, mIsPunctuationSuggestions, mIsObsoleteSuggestions,
mIsPrediction);
}
+
+ // Creates a new SuggestedWordInfo from the currently suggested words that removes all but the
+ // last word of all suggestions, separated by a space. This is necessary because when we commit
+ // a multiple-word suggestion, the IME only retains the last word as the composing word, and
+ // we should only suggest replacements for this last word.
+ // TODO: make this work with languages without spaces.
+ public SuggestedWords getSuggestedWordsForLastWordOfPhraseGesture() {
+ final ArrayList<SuggestedWordInfo> newSuggestions = CollectionUtils.newArrayList();
+ for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) {
+ final SuggestedWordInfo info = mSuggestedWordInfoList.get(i);
+ final int indexOfLastSpace = info.mWord.lastIndexOf(Constants.CODE_SPACE) + 1;
+ final String lastWord = info.mWord.substring(indexOfLastSpace);
+ newSuggestions.add(new SuggestedWordInfo(lastWord, info.mScore, info.mKind,
+ info.mSourceDict, SuggestedWordInfo.NOT_AN_INDEX,
+ SuggestedWordInfo.NOT_A_CONFIDENCE));
+ }
+ return new SuggestedWords(newSuggestions, mTypedWordValid,
+ mWillAutoCorrect, mIsPunctuationSuggestions, mIsObsoleteSuggestions,
+ mIsPrediction);
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
index f333b0d86..70931f885 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
@@ -758,8 +758,15 @@ public class BinaryDictEncoderUtils {
final FormatOptions formatOptions) {
int positionOfChildrenPosField = ptNode.mCachedAddressAfterUpdate
+ getNodeHeaderSize(ptNode, formatOptions);
- if (ptNode.mFrequency >= 0) {
- positionOfChildrenPosField += FormatSpec.PTNODE_FREQUENCY_SIZE;
+ if (ptNode.isTerminal()) {
+ // A terminal node has either the terminal id or the frequency.
+ // If positionOfChildrenPosField is incorrect, we may crash when jumping to the children
+ // position.
+ if (formatOptions.mHasTerminalId) {
+ positionOfChildrenPosField += FormatSpec.PTNODE_TERMINAL_ID_SIZE;
+ } else {
+ positionOfChildrenPosField += FormatSpec.PTNODE_FREQUENCY_SIZE;
+ }
}
return null == ptNode.mChildren ? FormatSpec.NO_CHILDREN_ADDRESS
: ptNode.mChildren.mCachedAddressAfterUpdate - positionOfChildrenPosField;
diff --git a/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java b/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java
index 4c7739a7a..7c6fe93ac 100644
--- a/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java
+++ b/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java
@@ -132,6 +132,15 @@ public final class ResizableIntArray {
}
}
+ /**
+ * Shift to the left by elementCount, discarding elementCount pointers at the start.
+ * @param elementCount how many elements to shift.
+ */
+ public void shift(final int elementCount) {
+ System.arraycopy(mArray, elementCount, mArray, 0, mLength - elementCount);
+ mLength -= elementCount;
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index be4184093..121aecf0f 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -16,16 +16,25 @@
package com.android.inputmethod.latin.utils;
-import android.text.TextUtils;
-
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.settings.SettingsValues;
+import android.text.TextUtils;
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Locale;
public final class StringUtils {
+ private static final String TAG = StringUtils.class.getSimpleName();
public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
public static final int CAPITALIZE_FIRST = 1; // First only
public static final int CAPITALIZE_ALL = 2; // All caps
@@ -390,4 +399,67 @@ public final class StringUtils {
}
return bytes;
}
+
+ public static List<Object> jsonStrToList(String s) {
+ final ArrayList<Object> retval = CollectionUtils.newArrayList();
+ final JsonReader reader = new JsonReader(new StringReader(s));
+ try {
+ reader.beginArray();
+ while(reader.hasNext()) {
+ reader.beginObject();
+ while (reader.hasNext()) {
+ final String name = reader.nextName();
+ if (name.equals(Integer.class.getSimpleName())) {
+ retval.add(reader.nextInt());
+ } else if (name.equals(String.class.getSimpleName())) {
+ retval.add(reader.nextString());
+ } else {
+ Log.w(TAG, "Invalid name: " + name);
+ reader.skipValue();
+ }
+ }
+ reader.endObject();
+ }
+ reader.endArray();
+ return retval;
+ } catch (IOException e) {
+ } finally {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ }
+ }
+ return Collections.<Object>emptyList();
+ }
+
+ public static String listToJsonStr(List<Object> list) {
+ if (list == null || list.isEmpty()) {
+ return "";
+ }
+ final StringWriter sw = new StringWriter();
+ final JsonWriter writer = new JsonWriter(sw);
+ try {
+ writer.beginArray();
+ for (final Object o : list) {
+ writer.beginObject();
+ if (o instanceof Integer) {
+ writer.name(Integer.class.getSimpleName()).value((Integer)o);
+ } else if (o instanceof String) {
+ writer.name(String.class.getSimpleName()).value((String)o);
+ }
+ writer.endObject();
+ }
+ writer.endArray();
+ return sw.toString();
+ } catch (IOException e) {
+ } finally {
+ try {
+ if (writer != null) {
+ writer.close();
+ }
+ } catch (IOException e) {
+ }
+ }
+ return "";
+ }
}