aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java58
-rw-r--r--java/src/com/android/inputmethod/latin/Constants.java4
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java8
-rw-r--r--java/src/com/android/inputmethod/latin/DicTraverseSession.java4
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitator.java9
-rw-r--r--java/src/com/android/inputmethod/latin/PrevWordsInfo.java119
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java2
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java31
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java16
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java3
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java10
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java50
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java6
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java49
-rw-r--r--java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java13
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ScriptUtils.java103
-rw-r--r--native/jni/NativeFileList.mk1
-rw-r--r--native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp32
-rw-r--r--native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp108
-rw-r--r--native/jni/src/suggest/core/dictionary/bigram_dictionary.h47
-rw-r--r--native/jni/src/suggest/core/dictionary/dictionary.cpp44
-rw-r--r--native/jni/src/suggest/core/dictionary/dictionary.h4
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp4
-rw-r--r--tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java5
-rw-r--r--tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java9
-rw-r--r--tests/src/com/android/inputmethod/latin/InputLogicTests.java14
-rw-r--r--tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java18
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java3
-rw-r--r--tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java7
29 files changed, 399 insertions, 382 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 335e52fef..95e1340ed 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -174,7 +174,7 @@ public final class BinaryDictionary extends Dictionary {
private static native int getFormatVersionNative(long dict);
private static native int getProbabilityNative(long dict, int[] word);
private static native int getMaxProbabilityOfExactMatchesNative(long dict, int[] word);
- private static native int getBigramProbabilityNative(long dict, int[] word0,
+ private static native int getNgramProbabilityNative(long dict, int[] word0,
boolean isBeginningOfSentence, int[] word1);
private static native void getWordPropertyNative(long dict, int[] word,
boolean isBeginningOfSentence, int[] outCodePoints, boolean[] outFlags,
@@ -190,13 +190,13 @@ public final class BinaryDictionary extends Dictionary {
int[] outputSuggestionCount, int[] outputCodePoints, int[] outputScores,
int[] outputIndices, int[] outputTypes, int[] outputAutoCommitFirstWordConfidence,
float[] inOutLanguageWeight);
- private static native boolean addUnigramWordNative(long dict, int[] word, int probability,
+ private static native boolean addUnigramEntryNative(long dict, int[] word, int probability,
int[] shortcutTarget, int shortcutProbability, boolean isBeginningOfSentence,
boolean isNotAWord, boolean isBlacklisted, int timestamp);
- private static native boolean removeUnigramWordNative(long dict, int[] word);
- private static native boolean addBigramWordsNative(long dict, int[] word0,
+ private static native boolean removeUnigramEntryNative(long dict, int[] word);
+ private static native boolean addNgramEntryNative(long dict, int[] word0,
boolean isBeginningOfSentence, int[] word1, int probability, int timestamp);
- private static native boolean removeBigramWordsNative(long dict, int[] word0,
+ private static native boolean removeNgramEntryNative(long dict, int[] word0,
boolean isBeginningOfSentence, int[] word1);
private static native int addMultipleDictionaryEntriesNative(long dict,
LanguageModelParam[] languageModelParams, int startIndex);
@@ -262,9 +262,8 @@ public final class BinaryDictionary extends Dictionary {
}
final DicTraverseSession session = getTraverseSession(sessionId);
Arrays.fill(session.mInputCodePoints, Constants.NOT_A_CODE);
- // TODO: toLowerCase in the native code
- final int[] prevWordCodePointArray = (null == prevWordsInfo.mPrevWord)
- ? null : StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
+ prevWordsInfo.outputToArray(session.mPrevWordCodePointArrays,
+ session.mIsBeginningOfSentenceArray);
final InputPointers inputPointers = composer.getInputPointers();
final boolean isGesture = composer.isBatchMode();
final int inputSize;
@@ -286,13 +285,13 @@ public final class BinaryDictionary extends Dictionary {
} else {
session.mInputOutputLanguageWeight[0] = Dictionary.NOT_A_LANGUAGE_WEIGHT;
}
- // proximityInfo and/or prevWordForBigrams may not be null.
+ // TOOD: Pass multiple previous words information for n-gram.
getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(),
getTraverseSession(sessionId).getSession(), inputPointers.getXCoordinates(),
inputPointers.getYCoordinates(), inputPointers.getTimes(),
inputPointers.getPointerIds(), session.mInputCodePoints, inputSize,
- session.mNativeSuggestOptions.getOptions(), prevWordCodePointArray,
- prevWordsInfo.mIsBeginningOfSentence, session.mOutputSuggestionCount,
+ session.mNativeSuggestOptions.getOptions(), session.mPrevWordCodePointArrays[0],
+ session.mIsBeginningOfSentenceArray[0], session.mOutputSuggestionCount,
session.mOutputCodePoints, session.mOutputScores, session.mSpaceIndices,
session.mOutputTypes, session.mOutputAutoCommitFirstWordConfidence,
session.mInputOutputLanguageWeight);
@@ -355,10 +354,13 @@ public final class BinaryDictionary extends Dictionary {
if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) {
return NOT_A_PROBABILITY;
}
- final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
- final int[] codePoints1 = StringUtils.toCodePointArray(word);
- return getBigramProbabilityNative(mNativeDict, codePoints0,
- prevWordsInfo.mIsBeginningOfSentence, codePoints1);
+ final int[][] prevWordCodePointArrays = new int[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM][];
+ final boolean[] isBeginningOfSentenceArray =
+ new boolean[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM];
+ prevWordsInfo.outputToArray(prevWordCodePointArrays, isBeginningOfSentenceArray);
+ final int[] wordCodePoints = StringUtils.toCodePointArray(word);
+ return getNgramProbabilityNative(mNativeDict, prevWordCodePointArrays[0],
+ isBeginningOfSentenceArray[0], wordCodePoints);
}
public WordProperty getWordProperty(final String word, final boolean isBeginningOfSentence) {
@@ -422,7 +424,7 @@ public final class BinaryDictionary extends Dictionary {
final int[] codePoints = StringUtils.toCodePointArray(word);
final int[] shortcutTargetCodePoints = (shortcutTarget != null) ?
StringUtils.toCodePointArray(shortcutTarget) : null;
- if (!addUnigramWordNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints,
+ if (!addUnigramEntryNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints,
shortcutProbability, isBeginningOfSentence, isNotAWord, isBlacklisted, timestamp)) {
return false;
}
@@ -436,7 +438,7 @@ public final class BinaryDictionary extends Dictionary {
return false;
}
final int[] codePoints = StringUtils.toCodePointArray(word);
- if (!removeUnigramWordNative(mNativeDict, codePoints)) {
+ if (!removeUnigramEntryNative(mNativeDict, codePoints)) {
return false;
}
mHasUpdated = true;
@@ -449,10 +451,13 @@ public final class BinaryDictionary extends Dictionary {
if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) {
return false;
}
- final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
- final int[] codePoints1 = StringUtils.toCodePointArray(word);
- if (!addBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence,
- codePoints1, probability, timestamp)) {
+ final int[][] prevWordCodePointArrays = new int[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM][];
+ final boolean[] isBeginningOfSentenceArray =
+ new boolean[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM];
+ prevWordsInfo.outputToArray(prevWordCodePointArrays, isBeginningOfSentenceArray);
+ final int[] wordCodePoints = StringUtils.toCodePointArray(word);
+ if (!addNgramEntryNative(mNativeDict, prevWordCodePointArrays[0],
+ isBeginningOfSentenceArray[0], wordCodePoints, probability, timestamp)) {
return false;
}
mHasUpdated = true;
@@ -464,10 +469,13 @@ public final class BinaryDictionary extends Dictionary {
if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) {
return false;
}
- final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
- final int[] codePoints1 = StringUtils.toCodePointArray(word);
- if (!removeBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence,
- codePoints1)) {
+ final int[][] prevWordCodePointArrays = new int[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM][];
+ final boolean[] isBeginningOfSentenceArray =
+ new boolean[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM];
+ prevWordsInfo.outputToArray(prevWordCodePointArrays, isBeginningOfSentenceArray);
+ final int[] wordCodePoints = StringUtils.toCodePointArray(word);
+ if (!removeNgramEntryNative(mNativeDict, prevWordCodePointArrays[0],
+ isBeginningOfSentenceArray[0], wordCodePoints)) {
return false;
}
mHasUpdated = true;
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index f9339361a..b4e115c7d 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -166,6 +166,10 @@ public final class Constants {
// Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h
public static final int DICTIONARY_MAX_WORD_LENGTH = 48;
+ // (MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1)-gram is supported in Java side. Needs to modify
+ // MAX_PREV_WORD_COUNT_FOR_N_GRAM in native/jni/src/defines.h for suggestions.
+ public static final int MAX_PREV_WORD_COUNT_FOR_N_GRAM = 2;
+
// Key events coming any faster than this are long-presses.
public static final int LONG_PRESS_MILLISECONDS = 200;
// TODO: Set this value appropriately.
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index 96160fa4e..ad14c06ef 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -233,19 +233,19 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
final int wordLen = StringUtils.codePointCount(word);
if (wordLen < MAX_WORD_LENGTH && wordLen > 1) {
if (DEBUG) {
- Log.d(TAG, "addName " + name + ", " + word + ", "
- + prevWordsInfo.mPrevWord);
+ Log.d(TAG, "addName " + name + ", " + word + ", " + prevWordsInfo);
}
runGCIfRequiredLocked(true /* mindsBlockByGC */);
addUnigramLocked(word, FREQUENCY_FOR_CONTACTS,
null /* shortcut */, 0 /* shortcutFreq */, false /* isNotAWord */,
false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP);
- if (!TextUtils.isEmpty(prevWordsInfo.mPrevWord) && mUseFirstLastBigrams) {
+ if (!prevWordsInfo.isValid() && mUseFirstLastBigrams) {
runGCIfRequiredLocked(true /* mindsBlockByGC */);
addNgramEntryLocked(prevWordsInfo, word, FREQUENCY_FOR_CONTACTS_BIGRAM,
BinaryDictionary.NOT_A_VALID_TIMESTAMP);
}
- prevWordsInfo = new PrevWordsInfo(word);
+ prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(
+ new PrevWordsInfo.WordInfo(word));
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
index 8bbf426e5..b341f623e 100644
--- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java
+++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
@@ -28,6 +28,10 @@ public final class DicTraverseSession {
// Must be equal to MAX_RESULTS in native/jni/src/defines.h
private static final int MAX_RESULTS = 18;
public final int[] mInputCodePoints = new int[Constants.DICTIONARY_MAX_WORD_LENGTH];
+ public final int[][] mPrevWordCodePointArrays =
+ new int[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM][];
+ public final boolean[] mIsBeginningOfSentenceArray =
+ new boolean[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM];
public final int[] mOutputSuggestionCount = new int[1];
public final int[] mOutputCodePoints =
new int[Constants.DICTIONARY_MAX_WORD_LENGTH * MAX_RESULTS];
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 304c450ab..b8feb2278 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -23,6 +23,7 @@ import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.PrevWordsInfo.WordInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.personalization.ContextualDictionary;
import com.android.inputmethod.latin.personalization.PersonalizationDataChunk;
@@ -407,13 +408,14 @@ public class DictionaryFacilitator {
final boolean blockPotentiallyOffensive) {
final Dictionaries dictionaries = mDictionaries;
final String[] words = suggestion.split(Constants.WORD_SEPARATOR);
+ PrevWordsInfo prevWordsInfoForCurrentWord = prevWordsInfo;
for (int i = 0; i < words.length; i++) {
final String currentWord = words[i];
- final PrevWordsInfo prevWordsInfoForCurrentWord =
- (i == 0) ? prevWordsInfo : new PrevWordsInfo(words[i - 1]);
final boolean wasCurrentWordAutoCapitalized = (i == 0) ? wasAutoCapitalized : false;
addWordToUserHistory(dictionaries, prevWordsInfoForCurrentWord, currentWord,
wasCurrentWordAutoCapitalized, timeStampInSeconds, blockPotentiallyOffensive);
+ prevWordsInfoForCurrentWord =
+ prevWordsInfoForCurrentWord.getNextPrevWordsInfo(new WordInfo(currentWord));
}
}
@@ -639,7 +641,8 @@ public class DictionaryFacilitator {
contextualDict.addNgramEntry(prevWordsInfo, phrase[i],
bigramProbabilityForWords, BinaryDictionary.NOT_A_VALID_TIMESTAMP);
}
- prevWordsInfo = new PrevWordsInfo(phrase[i]);
+ prevWordsInfo =
+ prevWordsInfo.getNextPrevWordsInfo(new PrevWordsInfo.WordInfo(phrase[i]));
}
}
diff --git a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
index 42b311c69..5dda44445 100644
--- a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
+++ b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
@@ -16,47 +16,122 @@
package com.android.inputmethod.latin;
+import java.util.Arrays;
+
+import com.android.inputmethod.latin.utils.StringUtils;
+
/**
* Class to represent information of previous words. This class is used to add n-gram entries
* into binary dictionaries, to get predictions, and to get suggestions.
*/
-// TODO: Support multiple previous words for n-gram.
public class PrevWordsInfo {
- public static final PrevWordsInfo EMPTY_PREV_WORDS_INFO = new PrevWordsInfo(null);
+ public static final PrevWordsInfo EMPTY_PREV_WORDS_INFO =
+ new PrevWordsInfo(WordInfo.EMPTY_WORD_INFO);
public static final PrevWordsInfo BEGINNING_OF_SENTENCE = new PrevWordsInfo();
- // The word immediately before the considered word. null means we don't have any context
- // including the "beginning of sentence context" - we just don't know what to predict.
- // An example of that is after a comma.
- // For simplicity of implementation, this may also be null transiently after the WordComposer
- // was reset and before starting a new composing word, but we should never be calling
- // getSuggetions* in this situation.
- // This is an empty string when mIsBeginningOfSentence is true.
- public final String mPrevWord;
+ /**
+ * Word information used to represent previous words information.
+ */
+ public static class WordInfo {
+ public static final WordInfo EMPTY_WORD_INFO = new WordInfo(null);
+ public static final WordInfo BEGINNING_OF_SENTENCE = new WordInfo();
+
+ // This is an empty string when mIsBeginningOfSentence is true.
+ public final String mWord;
+ // TODO: Have sentence separator.
+ // Whether the current context is beginning of sentence or not. This is true when composing
+ // at the beginning of an input field or composing a word after a sentence separator.
+ public final boolean mIsBeginningOfSentence;
+
+ // Beginning of sentence.
+ public WordInfo() {
+ mWord = "";
+ mIsBeginningOfSentence = true;
+ }
+
+ public WordInfo(final String word) {
+ mWord = word;
+ mIsBeginningOfSentence = false;
+ }
+
+ public boolean isValid() {
+ return mWord != null;
+ }
+ }
- // TODO: Have sentence separator.
- // Whether the current context is beginning of sentence or not. This is true when composing at
- // the beginning of an input field or composing a word after a sentence separator.
- public final boolean mIsBeginningOfSentence;
+ // The words immediately before the considered word. EMPTY_WORD_INFO element means we don't
+ // have any context for that previous word including the "beginning of sentence context" - we
+ // just don't know what to predict using the information. An example of that is after a comma.
+ // For simplicity of implementation, elements may also be EMPTY_WORD_INFO transiently after the
+ // WordComposer was reset and before starting a new composing word, but we should never be
+ // calling getSuggetions* in this situation.
+ public WordInfo[] mPrevWordsInfo = new WordInfo[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM];
// Beginning of sentence.
public PrevWordsInfo() {
- mPrevWord = "";
- mIsBeginningOfSentence = true;
+ mPrevWordsInfo[0] = WordInfo.BEGINNING_OF_SENTENCE;
+ Arrays.fill(mPrevWordsInfo, 1 /* start */, mPrevWordsInfo.length, WordInfo.EMPTY_WORD_INFO);
}
- public PrevWordsInfo(final String prevWord) {
- mPrevWord = prevWord;
- mIsBeginningOfSentence = false;
+ // Construct from the previous word information.
+ public PrevWordsInfo(final WordInfo prevWordInfo) {
+ mPrevWordsInfo[0] = prevWordInfo;
+ Arrays.fill(mPrevWordsInfo, 1 /* start */, mPrevWordsInfo.length, WordInfo.EMPTY_WORD_INFO);
+ }
+
+ // Construct from WordInfo array. n-th element represents (n+1)-th previous word's information.
+ public PrevWordsInfo(final WordInfo[] prevWordsInfo) {
+ for (int i = 0; i < Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM; i++) {
+ mPrevWordsInfo[i] =
+ (prevWordsInfo.length > i) ? prevWordsInfo[i] : WordInfo.EMPTY_WORD_INFO;
+ }
+ }
+
+ // Create next prevWordsInfo using current prevWordsInfo.
+ public PrevWordsInfo getNextPrevWordsInfo(final WordInfo wordInfo) {
+ final WordInfo[] prevWordsInfo = new WordInfo[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM];
+ prevWordsInfo[0] = wordInfo;
+ for (int i = 1; i < prevWordsInfo.length; i++) {
+ prevWordsInfo[i] = mPrevWordsInfo[i - 1];
+ }
+ return new PrevWordsInfo(prevWordsInfo);
}
public boolean isValid() {
- return mPrevWord != null;
+ return mPrevWordsInfo[0].isValid();
+ }
+
+ public void outputToArray(final int[][] codePointArrays,
+ final boolean[] isBeginningOfSentenceArray) {
+ for (int i = 0; i < mPrevWordsInfo.length; i++) {
+ final WordInfo wordInfo = mPrevWordsInfo[i];
+ if (wordInfo == null || !wordInfo.isValid()) {
+ codePointArrays[i] = new int[0];
+ isBeginningOfSentenceArray[i] = false;
+ continue;
+ }
+ codePointArrays[i] = StringUtils.toCodePointArray(wordInfo.mWord);
+ isBeginningOfSentenceArray[i] = wordInfo.mIsBeginningOfSentence;
+ }
}
@Override
public String toString() {
- return "PrevWord: " + mPrevWord + ", isBeginningOfSentence: "
- + mIsBeginningOfSentence + ".";
+ final StringBuffer builder = new StringBuffer();
+ for (int i = 0; i < mPrevWordsInfo.length; i++) {
+ final WordInfo wordInfo = mPrevWordsInfo[i];
+ builder.append("PrevWord[");
+ builder.append(i);
+ builder.append("]: ");
+ if (!wordInfo.isValid()) {
+ builder.append("Empty. ");
+ continue;
+ }
+ builder.append(wordInfo.mWord);
+ builder.append(", isBeginningOfSentence: ");
+ builder.append(wordInfo.mIsBeginningOfSentence);
+ builder.append(". ");
+ }
+ return builder.toString();
}
}
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 96476b2ee..3be6bccc6 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -603,7 +603,7 @@ public final class RichInputConnection {
|| spacingAndPunctuations.isWordConnector(lastChar)) {
return PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
}
- return new PrevWordsInfo(nthPrevWord);
+ return new PrevWordsInfo(new PrevWordsInfo.WordInfo(nthPrevWord));
}
/**
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 9d03e8a43..2a56861b4 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -123,21 +123,15 @@ public final class Suggest {
suggestionsContainer.set(i, transformedWordInfo);
}
}
- SuggestedWordInfo.removeDups(typedWord, suggestionsContainer);
+ final boolean didRemoveTypedWord =
+ SuggestedWordInfo.removeDups(typedWord, suggestionsContainer);
- // If resumed, then we don't want to upcase everything: resuming on a fully-capitalized
- // words is rarely done to switch to another fully-capitalized word, but usually to a
- // normal, non-capitalized suggestion.
- final String firstSuggestion;
final String whitelistedWord;
- if (suggestionResults.isEmpty()) {
+ if (suggestionsContainer.isEmpty()) {
whitelistedWord = firstSuggestion = null;
} else {
- final SuggestedWordInfo firstSuggestedWordInfo = getTransformedSuggestedWordInfo(
- suggestionResults.first(), suggestionResults.mLocale,
- shouldMakeSuggestionsAllUpperCase, isOnlyFirstCharCapitalized,
- trailingSingleQuotesCount);
- firstSuggestion = firstSuggestedWordInfo.mWord;
+ final SuggestedWordInfo firstSuggestedWordInfo = suggestionsContainer.get(0);
+ final String firstSuggestion = firstSuggestedWordInfo.mWord;
if (!firstSuggestedWordInfo.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) {
whitelistedWord = null;
} else {
@@ -145,17 +139,10 @@ public final class Suggest {
}
}
- // We allow auto-correction if we have a whitelisted word, or if the word is not a valid
- // word of more than 1 char, except if the first suggestion is the same as the typed string
- // because in this case if it's strong enough to auto-correct that will mistakenly designate
- // the second candidate for auto-correction.
- // TODO: stop relying on indices to find where is the auto-correction in the suggested
- // words, and correct this test.
- final boolean allowsToBeAutoCorrected = (null != whitelistedWord
- && !whitelistedWord.equals(typedWord))
- || (consideredWord.length() > 1 && !mDictionaryFacilitator.isValidWord(
- consideredWord, isOnlyFirstCharCapitalized)
- && !typedWord.equals(firstSuggestion));
+ // We allow auto-correction if we have a whitelisted word, or if the word had more than
+ // one char and was not suggested.
+ final boolean allowsToBeAutoCorrected = (null != whitelistedWord)
+ || (consideredWord.length() > 1 && !didRemoveTypedWord);
final boolean hasAutoCorrection;
// TODO: using isCorrectionEnabled here is not very good. It's probably useless, because
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index f22af7991..e587b18c9 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -330,29 +330,37 @@ public class SuggestedWords {
}
// This will always remove the higher index if a duplicate is found.
- public static void removeDups(final String typedWord,
+ public static boolean removeDups(final String typedWord,
ArrayList<SuggestedWordInfo> candidates) {
if (candidates.isEmpty()) {
- return;
+ return false;
}
+ final boolean didRemoveTypedWord;
if (!TextUtils.isEmpty(typedWord)) {
- removeSuggestedWordInfoFrom(typedWord, candidates, -1 /* startIndexExclusive */);
+ didRemoveTypedWord = removeSuggestedWordInfoFrom(typedWord, candidates,
+ -1 /* startIndexExclusive */);
+ } else {
+ didRemoveTypedWord = false;
}
for (int i = 0; i < candidates.size(); ++i) {
removeSuggestedWordInfoFrom(candidates.get(i).mWord, candidates,
i /* startIndexExclusive */);
}
+ return didRemoveTypedWord;
}
- private static void removeSuggestedWordInfoFrom(final String word,
+ private static boolean removeSuggestedWordInfoFrom(final String word,
final ArrayList<SuggestedWordInfo> candidates, final int startIndexExclusive) {
+ boolean didRemove = false;
for (int i = startIndexExclusive + 1; i < candidates.size(); ++i) {
final SuggestedWordInfo previous = candidates.get(i);
if (word.equals(previous.mWord)) {
+ didRemove = true;
candidates.remove(i);
--i;
}
}
+ return didRemove;
}
}
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index dd2a95ee0..ec57cd71f 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -1536,7 +1536,8 @@ public final class InputLogic {
} else {
return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ?
PrevWordsInfo.BEGINNING_OF_SENTENCE :
- new PrevWordsInfo(mLastComposedWord.mCommittedWord.toString());
+ new PrevWordsInfo(new PrevWordsInfo.WordInfo(
+ mLastComposedWord.mCommittedWord.toString()));
}
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
index 3916fc24c..a98b0f156 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
@@ -60,7 +60,7 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas
public static void addToDictionary(final ExpandableBinaryDictionary userHistoryDictionary,
final PrevWordsInfo prevWordsInfo, final String word, final boolean isValid,
final int timestamp, final DistracterFilter distracterFilter) {
- final String prevWord = prevWordsInfo.mPrevWord;
+ final String prevWord = prevWordsInfo.mPrevWordsInfo[0].mWord;
if (word.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH ||
(prevWord != null && prevWord.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) {
return;
@@ -75,7 +75,13 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas
return;
}
if (null != prevWord) {
- userHistoryDictionary.addNgramEntry(prevWordsInfo, word, frequency, timestamp);
+ if (prevWordsInfo.mPrevWordsInfo[0].mIsBeginningOfSentence) {
+ // Beginning-of-Sentence n-gram entry is treated as a n-gram entry of invalid word.
+ userHistoryDictionary.addNgramEntry(prevWordsInfo, word,
+ FREQUENCY_FOR_WORDS_NOT_IN_DICTS, timestamp);
+ } else {
+ userHistoryDictionary.addNgramEntry(prevWordsInfo, word, frequency, timestamp);
+ }
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 8d495646d..d0316242b 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -37,6 +37,7 @@ import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.LocaleUtils;
+import com.android.inputmethod.latin.utils.ScriptUtils;
import com.android.inputmethod.latin.utils.StringUtils;
import java.lang.ref.WeakReference;
@@ -78,40 +79,8 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
private final HashSet<WeakReference<DictionaryCollection>> mDictionaryCollectionsList =
new HashSet<>();
- public static final int SCRIPT_LATIN = 0;
- public static final int SCRIPT_CYRILLIC = 1;
- public static final int SCRIPT_GREEK = 2;
public static final String SINGLE_QUOTE = "\u0027";
public static final String APOSTROPHE = "\u2019";
- private static final TreeMap<String, Integer> mLanguageToScript;
- static {
- // List of the supported languages and their associated script. We won't check
- // words written in another script than the selected script, because we know we
- // don't have those in our dictionary so we will underline everything and we
- // will never have any suggestions, so it makes no sense checking them, and this
- // is done in {@link #shouldFilterOut}. Also, the script is used to choose which
- // proximity to pass to the dictionary descent algorithm.
- // IMPORTANT: this only contains languages - do not write countries in there.
- // Only the language is searched from the map.
- mLanguageToScript = new TreeMap<>();
- mLanguageToScript.put("cs", SCRIPT_LATIN);
- mLanguageToScript.put("da", SCRIPT_LATIN);
- mLanguageToScript.put("de", SCRIPT_LATIN);
- mLanguageToScript.put("el", SCRIPT_GREEK);
- mLanguageToScript.put("en", SCRIPT_LATIN);
- mLanguageToScript.put("es", SCRIPT_LATIN);
- mLanguageToScript.put("fi", SCRIPT_LATIN);
- mLanguageToScript.put("fr", SCRIPT_LATIN);
- mLanguageToScript.put("hr", SCRIPT_LATIN);
- mLanguageToScript.put("it", SCRIPT_LATIN);
- mLanguageToScript.put("lt", SCRIPT_LATIN);
- mLanguageToScript.put("lv", SCRIPT_LATIN);
- mLanguageToScript.put("nb", SCRIPT_LATIN);
- mLanguageToScript.put("nl", SCRIPT_LATIN);
- mLanguageToScript.put("pt", SCRIPT_LATIN);
- mLanguageToScript.put("sl", SCRIPT_LATIN);
- mLanguageToScript.put("ru", SCRIPT_CYRILLIC);
- }
@Override public void onCreate() {
super.onCreate();
@@ -122,22 +91,13 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY);
}
- public static int getScriptFromLocale(final Locale locale) {
- final Integer script = mLanguageToScript.get(locale.getLanguage());
- if (null == script) {
- throw new RuntimeException("We have been called with an unsupported language: \""
- + locale.getLanguage() + "\". Framework bug?");
- }
- return script;
- }
-
private static String getKeyboardLayoutNameForScript(final int script) {
switch (script) {
- case AndroidSpellCheckerService.SCRIPT_LATIN:
+ case ScriptUtils.SCRIPT_LATIN:
return "qwerty";
- case AndroidSpellCheckerService.SCRIPT_CYRILLIC:
+ case ScriptUtils.SCRIPT_CYRILLIC:
return "east_slavic";
- case AndroidSpellCheckerService.SCRIPT_GREEK:
+ case ScriptUtils.SCRIPT_GREEK:
return "greek";
default:
throw new RuntimeException("Wrong script supplied: " + script);
@@ -413,7 +373,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
}
public DictAndKeyboard createDictAndKeyboard(final Locale locale) {
- final int script = getScriptFromLocale(locale);
+ final int script = ScriptUtils.getScriptFromLocale(locale);
final String keyboardLayoutName = getKeyboardLayoutNameForScript(script);
final InputMethodSubtype subtype = AdditionalSubtypeUtils.createAdditionalSubtype(
locale.toString(), keyboardLayoutName, null);
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
index 55274cfe2..6bfd354ea 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
@@ -61,7 +61,8 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck
final int offset = ssi.getOffsetAt(i);
final int length = ssi.getLengthAt(i);
final String subText = typedText.substring(offset, offset + length);
- final PrevWordsInfo prevWordsInfo = new PrevWordsInfo(currentWord);
+ final PrevWordsInfo prevWordsInfo =
+ new PrevWordsInfo(new PrevWordsInfo.WordInfo(currentWord));
currentWord = subText;
if (!subText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) {
continue;
@@ -203,7 +204,8 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck
} else {
prevWord = null;
}
- final PrevWordsInfo prevWordsInfo = new PrevWordsInfo(prevWord);
+ final PrevWordsInfo prevWordsInfo =
+ new PrevWordsInfo(new PrevWordsInfo.WordInfo(prevWord));
retval[i] = onGetSuggestionsInternal(textInfos[i], prevWordsInfo, suggestionsLimit);
retval[i].setCookieAndSequence(textInfos[i].getCookie(),
textInfos[i].getSequence());
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 54eebe399..db15c1cc6 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -36,6 +36,7 @@ import com.android.inputmethod.latin.WordComposer;
import com.android.inputmethod.latin.spellcheck.AndroidSpellCheckerService.SuggestionsGatherer;
import com.android.inputmethod.latin.utils.CoordinateUtils;
import com.android.inputmethod.latin.utils.LocaleUtils;
+import com.android.inputmethod.latin.utils.ScriptUtils;
import com.android.inputmethod.latin.utils.StringUtils;
import java.util.ArrayList;
@@ -72,10 +73,10 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
// TODO: Support n-gram input
private static String generateKey(final String query, final PrevWordsInfo prevWordsInfo) {
- if (TextUtils.isEmpty(query) || TextUtils.isEmpty(prevWordsInfo.mPrevWord)) {
+ if (TextUtils.isEmpty(query) || !prevWordsInfo.isValid()) {
return query;
}
- return query + CHAR_DELIMITER + prevWordsInfo.mPrevWord;
+ return query + CHAR_DELIMITER + prevWordsInfo;
}
public SuggestionsParams getSuggestionsFromCache(String query,
@@ -116,7 +117,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
final String localeString = getLocale();
mDictionaryPool = mService.getDictionaryPool(localeString);
mLocale = LocaleUtils.constructLocaleFromString(localeString);
- mScript = AndroidSpellCheckerService.getScriptFromLocale(mLocale);
+ mScript = ScriptUtils.getScriptFromLocale(mLocale);
}
@Override
@@ -125,44 +126,6 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
cres.unregisterContentObserver(mObserver);
}
- /*
- * Returns whether the code point is a letter that makes sense for the specified
- * locale for this spell checker.
- * The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml
- * and is limited to EFIGS languages and Russian.
- * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters
- * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters.
- */
- private static boolean isLetterCheckableByLanguage(final int codePoint,
- final int script) {
- switch (script) {
- case AndroidSpellCheckerService.SCRIPT_LATIN:
- // Our supported latin script dictionaries (EFIGS) at the moment only include
- // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode
- // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF,
- // so the below is a very efficient way to test for it. As for the 0-0x3F, it's
- // excluded from isLetter anyway.
- return codePoint <= 0x2AF && Character.isLetter(codePoint);
- case AndroidSpellCheckerService.SCRIPT_CYRILLIC:
- // All Cyrillic characters are in the 400~52F block. There are some in the upper
- // Unicode range, but they are archaic characters that are not used in modern
- // Russian and are not used by our dictionary.
- return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint);
- case AndroidSpellCheckerService.SCRIPT_GREEK:
- // Greek letters are either in the 370~3FF range (Greek & Coptic), or in the
- // 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters.
- // Our dictionary also contains a few words with 0xF2; it would be best to check
- // if that's correct, but a web search does return results for these words so
- // they are probably okay.
- return (codePoint >= 0x370 && codePoint <= 0x3FF)
- || (codePoint >= 0x1F00 && codePoint <= 0x1FFF)
- || codePoint == 0xF2;
- default:
- // Should never come here
- throw new RuntimeException("Impossible value of script: " + script);
- }
- }
-
private static final int CHECKABILITY_CHECKABLE = 0;
private static final int CHECKABILITY_TOO_MANY_NON_LETTERS = 1;
private static final int CHECKABILITY_CONTAINS_PERIOD = 2;
@@ -189,7 +152,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
// Filter by first letter
final int firstCodePoint = text.codePointAt(0);
// Filter out words that don't start with a letter or an apostrophe
- if (!isLetterCheckableByLanguage(firstCodePoint, script)
+ if (!ScriptUtils.isLetterCheckableByScript(firstCodePoint, script)
&& '\'' != firstCodePoint) return CHECKABILITY_FIRST_LETTER_UNCHECKABLE;
// Filter contents
@@ -210,7 +173,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
if (Constants.CODE_PERIOD == codePoint) {
return CHECKABILITY_CONTAINS_PERIOD;
}
- if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount;
+ if (ScriptUtils.isLetterCheckableByScript(codePoint, script)) ++letterCount;
}
// Guestimate heuristic: perform spell checking if at least 3/4 of the characters
// in this word are letters
diff --git a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
index 4248bebf6..9ec19efa8 100644
--- a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
+++ b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
@@ -117,7 +117,8 @@ public final class LanguageModelParam {
continue;
}
languageModelParams.add(languageModelParam);
- prevWordsInfo = new PrevWordsInfo(languageModelParam.mTargetWord);
+ prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(
+ new PrevWordsInfo.WordInfo(tempWord));
}
return languageModelParams;
}
@@ -153,7 +154,7 @@ public final class LanguageModelParam {
final DistracterFilter distracterFilter) {
final String word;
if (StringUtils.getCapitalizationType(targetWord) == StringUtils.CAPITALIZE_FIRST
- && prevWordsInfo.mPrevWord == null && !isValidWord) {
+ && !prevWordsInfo.isValid() && !isValidWord) {
word = targetWord.toLowerCase(locale);
} else {
word = targetWord;
@@ -167,7 +168,7 @@ public final class LanguageModelParam {
}
final int unigramProbability = isValidWord ?
UNIGRAM_PROBABILITY_FOR_VALID_WORD : UNIGRAM_PROBABILITY_FOR_OOV_WORD;
- if (prevWordsInfo.mPrevWord == null) {
+ if (!prevWordsInfo.isValid()) {
if (DEBUG) {
Log.d(TAG, "--- add unigram: current("
+ (isValidWord ? "Valid" : "OOV") + ") = " + word);
@@ -175,12 +176,12 @@ public final class LanguageModelParam {
return new LanguageModelParam(word, unigramProbability, timestamp);
}
if (DEBUG) {
- Log.d(TAG, "--- add bigram: prev = " + prevWordsInfo.mPrevWord + ", current("
+ Log.d(TAG, "--- add bigram: prev = " + prevWordsInfo + ", current("
+ (isValidWord ? "Valid" : "OOV") + ") = " + word);
}
final int bigramProbability = isValidWord ?
BIGRAM_PROBABILITY_FOR_VALID_WORD : BIGRAM_PROBABILITY_FOR_OOV_WORD;
- return new LanguageModelParam(prevWordsInfo.mPrevWord, word, unigramProbability,
- bigramProbability, timestamp);
+ return new LanguageModelParam(prevWordsInfo.mPrevWordsInfo[0].mWord, word,
+ unigramProbability, bigramProbability, timestamp);
}
}
diff --git a/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java b/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java
new file mode 100644
index 000000000..4dfb38d80
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java
@@ -0,0 +1,103 @@
+/*
+ * 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.utils;
+
+import java.util.Locale;
+import java.util.TreeMap;
+
+/**
+ * A class to help with handling different writing scripts.
+ */
+public class ScriptUtils {
+ public static final int SCRIPT_LATIN = 0;
+ public static final int SCRIPT_CYRILLIC = 1;
+ public static final int SCRIPT_GREEK = 2;
+ public static final TreeMap<String, Integer> mLanguageToScript;
+ static {
+ // List of the supported languages and their associated script. We won't check
+ // words written in another script than the selected script, because we know we
+ // don't have those in our dictionary so we will underline everything and we
+ // will never have any suggestions, so it makes no sense checking them, and this
+ // is done in {@link #shouldFilterOut}. Also, the script is used to choose which
+ // proximity to pass to the dictionary descent algorithm.
+ // IMPORTANT: this only contains languages - do not write countries in there.
+ // Only the language is searched from the map.
+ mLanguageToScript = new TreeMap<>();
+ mLanguageToScript.put("cs", SCRIPT_LATIN);
+ mLanguageToScript.put("da", SCRIPT_LATIN);
+ mLanguageToScript.put("de", SCRIPT_LATIN);
+ mLanguageToScript.put("el", SCRIPT_GREEK);
+ mLanguageToScript.put("en", SCRIPT_LATIN);
+ mLanguageToScript.put("es", SCRIPT_LATIN);
+ mLanguageToScript.put("fi", SCRIPT_LATIN);
+ mLanguageToScript.put("fr", SCRIPT_LATIN);
+ mLanguageToScript.put("hr", SCRIPT_LATIN);
+ mLanguageToScript.put("it", SCRIPT_LATIN);
+ mLanguageToScript.put("lt", SCRIPT_LATIN);
+ mLanguageToScript.put("lv", SCRIPT_LATIN);
+ mLanguageToScript.put("nb", SCRIPT_LATIN);
+ mLanguageToScript.put("nl", SCRIPT_LATIN);
+ mLanguageToScript.put("pt", SCRIPT_LATIN);
+ mLanguageToScript.put("sl", SCRIPT_LATIN);
+ mLanguageToScript.put("ru", SCRIPT_CYRILLIC);
+ }
+ /*
+ * Returns whether the code point is a letter that makes sense for the specified
+ * locale for this spell checker.
+ * The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml
+ * and is limited to EFIGS languages and Russian.
+ * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters
+ * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters.
+ */
+ public static boolean isLetterCheckableByScript(final int codePoint, final int script) {
+ switch (script) {
+ case SCRIPT_LATIN:
+ // Our supported latin script dictionaries (EFIGS) at the moment only include
+ // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode
+ // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF,
+ // so the below is a very efficient way to test for it. As for the 0-0x3F, it's
+ // excluded from isLetter anyway.
+ return codePoint <= 0x2AF && Character.isLetter(codePoint);
+ case SCRIPT_CYRILLIC:
+ // All Cyrillic characters are in the 400~52F block. There are some in the upper
+ // Unicode range, but they are archaic characters that are not used in modern
+ // Russian and are not used by our dictionary.
+ return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint);
+ case SCRIPT_GREEK:
+ // Greek letters are either in the 370~3FF range (Greek & Coptic), or in the
+ // 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters.
+ // Our dictionary also contains a few words with 0xF2; it would be best to check
+ // if that's correct, but a web search does return results for these words so
+ // they are probably okay.
+ return (codePoint >= 0x370 && codePoint <= 0x3FF)
+ || (codePoint >= 0x1F00 && codePoint <= 0x1FFF)
+ || codePoint == 0xF2;
+ default:
+ // Should never come here
+ throw new RuntimeException("Impossible value of script: " + script);
+ }
+ }
+
+ public static int getScriptFromLocale(final Locale locale) {
+ final Integer script = mLanguageToScript.get(locale.getLanguage());
+ if (null == script) {
+ throw new RuntimeException("We have been called with an unsupported language: \""
+ + locale.getLanguage() + "\". Framework bug?");
+ }
+ return script;
+ }
+}
diff --git a/native/jni/NativeFileList.mk b/native/jni/NativeFileList.mk
index 2dd75c4f5..fe2106140 100644
--- a/native/jni/NativeFileList.mk
+++ b/native/jni/NativeFileList.mk
@@ -26,7 +26,6 @@ LATIN_IME_CORE_SRC_FILES := \
dic_node_utils.cpp \
dic_nodes_cache.cpp) \
$(addprefix suggest/core/dictionary/, \
- bigram_dictionary.cpp \
dictionary.cpp \
dictionary_utils.cpp \
digraph_utils.cpp \
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 2654a4a0a..6b4fb7986 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -283,7 +283,7 @@ static jint latinime_BinaryDictionary_getMaxProbabilityOfExactMatches(
return dictionary->getMaxProbabilityOfExactMatches(codePoints, wordLength);
}
-static jint latinime_BinaryDictionary_getBigramProbability(JNIEnv *env, jclass clazz,
+static jint latinime_BinaryDictionary_getNgramProbability(JNIEnv *env, jclass clazz,
jlong dict, jintArray word0, jboolean isBeginningOfSentence, jintArray word1) {
Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
if (!dictionary) return JNI_FALSE;
@@ -294,7 +294,7 @@ static jint latinime_BinaryDictionary_getBigramProbability(JNIEnv *env, jclass c
env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints);
env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints);
const PrevWordsInfo prevWordsInfo(word0CodePoints, word0Length, isBeginningOfSentence);
- return dictionary->getBigramProbability(&prevWordsInfo, word1CodePoints, word1Length);
+ return dictionary->getNgramProbability(&prevWordsInfo, word1CodePoints, word1Length);
}
// Method to iterate all words in the dictionary for makedict.
@@ -355,7 +355,7 @@ static void latinime_BinaryDictionary_getWordProperty(JNIEnv *env, jclass clazz,
outShortcutProbabilities);
}
-static bool latinime_BinaryDictionary_addUnigramWord(JNIEnv *env, jclass clazz, jlong dict,
+static bool latinime_BinaryDictionary_addUnigramEntry(JNIEnv *env, jclass clazz, jlong dict,
jintArray word, jint probability, jintArray shortcutTarget, jint shortcutProbability,
jboolean isBeginningOfSentence, jboolean isNotAWord, jboolean isBlacklisted,
jint timestamp) {
@@ -378,7 +378,7 @@ static bool latinime_BinaryDictionary_addUnigramWord(JNIEnv *env, jclass clazz,
return dictionary->addUnigramEntry(codePoints, codePointCount, &unigramProperty);
}
-static bool latinime_BinaryDictionary_removeUnigramWord(JNIEnv *env, jclass clazz, jlong dict,
+static bool latinime_BinaryDictionary_removeUnigramEntry(JNIEnv *env, jclass clazz, jlong dict,
jintArray word) {
Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
if (!dictionary) {
@@ -390,7 +390,7 @@ static bool latinime_BinaryDictionary_removeUnigramWord(JNIEnv *env, jclass claz
return dictionary->removeUnigramEntry(codePoints, codePointCount);
}
-static bool latinime_BinaryDictionary_addBigramWords(JNIEnv *env, jclass clazz, jlong dict,
+static bool latinime_BinaryDictionary_addNgramEntry(JNIEnv *env, jclass clazz, jlong dict,
jintArray word0, jboolean isBeginningOfSentence, jintArray word1, jint probability,
jint timestamp) {
Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
@@ -412,7 +412,7 @@ static bool latinime_BinaryDictionary_addBigramWords(JNIEnv *env, jclass clazz,
return dictionary->addNgramEntry(&prevWordsInfo, &bigramProperty);
}
-static bool latinime_BinaryDictionary_removeBigramWords(JNIEnv *env, jclass clazz, jlong dict,
+static bool latinime_BinaryDictionary_removeNgramEntry(JNIEnv *env, jclass clazz, jlong dict,
jintArray word0, jboolean isBeginningOfSentence, jintArray word1) {
Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
if (!dictionary) {
@@ -686,9 +686,9 @@ static const JNINativeMethod sMethods[] = {
reinterpret_cast<void *>(latinime_BinaryDictionary_getMaxProbabilityOfExactMatches)
},
{
- const_cast<char *>("getBigramProbabilityNative"),
+ const_cast<char *>("getNgramProbabilityNative"),
const_cast<char *>("(J[IZ[I)I"),
- reinterpret_cast<void *>(latinime_BinaryDictionary_getBigramProbability)
+ reinterpret_cast<void *>(latinime_BinaryDictionary_getNgramProbability)
},
{
const_cast<char *>("getWordPropertyNative"),
@@ -702,24 +702,24 @@ static const JNINativeMethod sMethods[] = {
reinterpret_cast<void *>(latinime_BinaryDictionary_getNextWord)
},
{
- const_cast<char *>("addUnigramWordNative"),
+ const_cast<char *>("addUnigramEntryNative"),
const_cast<char *>("(J[II[IIZZZI)Z"),
- reinterpret_cast<void *>(latinime_BinaryDictionary_addUnigramWord)
+ reinterpret_cast<void *>(latinime_BinaryDictionary_addUnigramEntry)
},
{
- const_cast<char *>("removeUnigramWordNative"),
+ const_cast<char *>("removeUnigramEntryNative"),
const_cast<char *>("(J[I)Z"),
- reinterpret_cast<void *>(latinime_BinaryDictionary_removeUnigramWord)
+ reinterpret_cast<void *>(latinime_BinaryDictionary_removeUnigramEntry)
},
{
- const_cast<char *>("addBigramWordsNative"),
+ const_cast<char *>("addNgramEntryNative"),
const_cast<char *>("(J[IZ[III)Z"),
- reinterpret_cast<void *>(latinime_BinaryDictionary_addBigramWords)
+ reinterpret_cast<void *>(latinime_BinaryDictionary_addNgramEntry)
},
{
- const_cast<char *>("removeBigramWordsNative"),
+ const_cast<char *>("removeNgramEntryNative"),
const_cast<char *>("(J[IZ[I)Z"),
- reinterpret_cast<void *>(latinime_BinaryDictionary_removeBigramWords)
+ reinterpret_cast<void *>(latinime_BinaryDictionary_removeNgramEntry)
},
{
const_cast<char *>("addMultipleDictionaryEntriesNative"),
diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
deleted file mode 100644
index 295e760d6..000000000
--- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2010, 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.
- */
-
-#define LOG_TAG "LatinIME: bigram_dictionary.cpp"
-
-#include "bigram_dictionary.h"
-
-#include <algorithm>
-#include <cstring>
-
-#include "defines.h"
-#include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h"
-#include "suggest/core/dictionary/dictionary.h"
-#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
-#include "suggest/core/result/suggestion_results.h"
-#include "suggest/core/session/prev_words_info.h"
-#include "utils/char_utils.h"
-
-namespace latinime {
-
-BigramDictionary::BigramDictionary(
- const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy)
- : mDictionaryStructurePolicy(dictionaryStructurePolicy) {
- if (DEBUG_DICT) {
- AKLOGI("BigramDictionary - constructor");
- }
-}
-
-BigramDictionary::~BigramDictionary() {
-}
-
-/* Parameters :
- * prevWordsInfo: Information of previous words to get the predictions.
- * outSuggestionResults: SuggestionResults to put the predictions.
- */
-void BigramDictionary::getPredictions(const PrevWordsInfo *const prevWordsInfo,
- SuggestionResults *const outSuggestionResults) const {
- int unigramProbability = 0;
- int bigramCodePoints[MAX_WORD_LENGTH];
- BinaryDictionaryBigramsIterator bigramsIt =
- prevWordsInfo->getBigramsIteratorForPrediction(mDictionaryStructurePolicy);
- while (bigramsIt.hasNext()) {
- bigramsIt.next();
- if (bigramsIt.getBigramPos() == NOT_A_DICT_POS) {
- continue;
- }
- const int codePointCount = mDictionaryStructurePolicy->
- getCodePointsAndProbabilityAndReturnCodePointCount(bigramsIt.getBigramPos(),
- MAX_WORD_LENGTH, bigramCodePoints, &unigramProbability);
- if (codePointCount <= 0) {
- continue;
- }
- // Due to space constraints, the probability for bigrams is approximate - the lower the
- // unigram probability, the worse the precision. The theoritical maximum error in
- // resulting probability is 8 - although in the practice it's never bigger than 3 or 4
- // in very bad cases. This means that sometimes, we'll see some bigrams interverted
- // here, but it can't get too bad.
- const int probability = mDictionaryStructurePolicy->getProbability(
- unigramProbability, bigramsIt.getProbability());
- outSuggestionResults->addPrediction(bigramCodePoints, codePointCount, probability);
- }
-}
-
-// Returns a pointer to the start of the bigram list.
-// If the word is not found or has no bigrams, this function returns NOT_A_DICT_POS.
-int BigramDictionary::getBigramListPositionForWord(const int *prevWord, const int prevWordLength,
- const bool forceLowerCaseSearch) const {
- if (0 >= prevWordLength) return NOT_A_DICT_POS;
- int pos = mDictionaryStructurePolicy->getTerminalPtNodePositionOfWord(prevWord, prevWordLength,
- forceLowerCaseSearch);
- if (NOT_A_DICT_POS == pos) return NOT_A_DICT_POS;
- return mDictionaryStructurePolicy->getBigramsPositionOfPtNode(pos);
-}
-
-int BigramDictionary::getBigramProbability(const PrevWordsInfo *const prevWordsInfo,
- const int *word1, int length1) const {
- int nextWordPos = mDictionaryStructurePolicy->getTerminalPtNodePositionOfWord(word1, length1,
- false /* forceLowerCaseSearch */);
- if (NOT_A_DICT_POS == nextWordPos) return NOT_A_PROBABILITY;
- BinaryDictionaryBigramsIterator bigramsIt =
- prevWordsInfo->getBigramsIteratorForPrediction(mDictionaryStructurePolicy);
- while (bigramsIt.hasNext()) {
- bigramsIt.next();
- if (bigramsIt.getBigramPos() == nextWordPos
- && bigramsIt.getProbability() != NOT_A_PROBABILITY) {
- return mDictionaryStructurePolicy->getProbability(
- mDictionaryStructurePolicy->getUnigramProbabilityOfPtNode(nextWordPos),
- bigramsIt.getProbability());
- }
- }
- return NOT_A_PROBABILITY;
-}
-
-// TODO: Move functions related to bigram to here
-} // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
deleted file mode 100644
index bd3aed1bd..000000000
--- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#ifndef LATINIME_BIGRAM_DICTIONARY_H
-#define LATINIME_BIGRAM_DICTIONARY_H
-
-#include "defines.h"
-
-namespace latinime {
-
-class DictionaryStructureWithBufferPolicy;
-class PrevWordsInfo;
-class SuggestionResults;
-
-class BigramDictionary {
- public:
- BigramDictionary(const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy);
-
- void getPredictions(const PrevWordsInfo *const prevWordsInfo,
- SuggestionResults *const outSuggestionResults) const;
- int getBigramProbability(const PrevWordsInfo *const prevWordsInfo,
- const int *word1, int length1) const;
- ~BigramDictionary();
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(BigramDictionary);
-
- int getBigramListPositionForWord(const int *prevWord, const int prevWordLength,
- const bool forceLowerCaseSearch) const;
-
- const DictionaryStructureWithBufferPolicy *const mDictionaryStructurePolicy;
-};
-} // namespace latinime
-#endif // LATINIME_BIGRAM_DICTIONARY_H
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp
index 228260216..fb25f757c 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp
@@ -23,6 +23,7 @@
#include "suggest/core/policy/dictionary_header_structure_policy.h"
#include "suggest/core/result/suggestion_results.h"
#include "suggest/core/session/dic_traverse_session.h"
+#include "suggest/core/session/prev_words_info.h"
#include "suggest/core/suggest.h"
#include "suggest/core/suggest_options.h"
#include "suggest/policyimpl/gesture/gesture_suggest_policy_factory.h"
@@ -37,7 +38,6 @@ const int Dictionary::HEADER_ATTRIBUTE_BUFFER_SIZE = 32;
Dictionary::Dictionary(JNIEnv *env, DictionaryStructureWithBufferPolicy::StructurePolicyPtr
dictionaryStructureWithBufferPolicy)
: mDictionaryStructureWithBufferPolicy(std::move(dictionaryStructureWithBufferPolicy)),
- mBigramDictionary(mDictionaryStructureWithBufferPolicy.get()),
mGestureSuggest(new Suggest(GestureSuggestPolicyFactory::getGestureSuggestPolicy())),
mTypingSuggest(new Suggest(TypingSuggestPolicyFactory::getTypingSuggestPolicy())) {
logDictionaryInfo(env);
@@ -62,7 +62,29 @@ void Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession
void Dictionary::getPredictions(const PrevWordsInfo *const prevWordsInfo,
SuggestionResults *const outSuggestionResults) const {
TimeKeeper::setCurrentTime();
- mBigramDictionary.getPredictions(prevWordsInfo, outSuggestionResults);
+ int unigramProbability = 0;
+ int bigramCodePoints[MAX_WORD_LENGTH];
+ BinaryDictionaryBigramsIterator bigramsIt = prevWordsInfo->getBigramsIteratorForPrediction(
+ mDictionaryStructureWithBufferPolicy.get());
+ while (bigramsIt.hasNext()) {
+ bigramsIt.next();
+ if (bigramsIt.getBigramPos() == NOT_A_DICT_POS) {
+ continue;
+ }
+ if (prevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */)
+ && bigramsIt.getProbability() == NOT_A_PROBABILITY) {
+ continue;
+ }
+ const int codePointCount = mDictionaryStructureWithBufferPolicy->
+ getCodePointsAndProbabilityAndReturnCodePointCount(bigramsIt.getBigramPos(),
+ MAX_WORD_LENGTH, bigramCodePoints, &unigramProbability);
+ if (codePointCount <= 0) {
+ continue;
+ }
+ const int probability = mDictionaryStructureWithBufferPolicy->getProbability(
+ unigramProbability, bigramsIt.getProbability());
+ outSuggestionResults->addPrediction(bigramCodePoints, codePointCount, probability);
+ }
}
int Dictionary::getProbability(const int *word, int length) const {
@@ -81,10 +103,24 @@ int Dictionary::getMaxProbabilityOfExactMatches(const int *word, int length) con
mDictionaryStructureWithBufferPolicy.get(), word, length);
}
-int Dictionary::getBigramProbability(const PrevWordsInfo *const prevWordsInfo, const int *word,
+int Dictionary::getNgramProbability(const PrevWordsInfo *const prevWordsInfo, const int *word,
int length) const {
TimeKeeper::setCurrentTime();
- return mBigramDictionary.getBigramProbability(prevWordsInfo, word, length);
+ int nextWordPos = mDictionaryStructureWithBufferPolicy->getTerminalPtNodePositionOfWord(word,
+ length, false /* forceLowerCaseSearch */);
+ if (NOT_A_DICT_POS == nextWordPos) return NOT_A_PROBABILITY;
+ BinaryDictionaryBigramsIterator bigramsIt = prevWordsInfo->getBigramsIteratorForPrediction(
+ mDictionaryStructureWithBufferPolicy.get());
+ while (bigramsIt.hasNext()) {
+ bigramsIt.next();
+ if (bigramsIt.getBigramPos() == nextWordPos
+ && bigramsIt.getProbability() != NOT_A_PROBABILITY) {
+ return mDictionaryStructureWithBufferPolicy->getProbability(
+ mDictionaryStructureWithBufferPolicy->getUnigramProbabilityOfPtNode(
+ nextWordPos), bigramsIt.getProbability());
+ }
+ }
+ return NOT_A_PROBABILITY;
}
bool Dictionary::addUnigramEntry(const int *const word, const int length,
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h
index 247ee2421..3b41088fe 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/dictionary.h
@@ -21,7 +21,6 @@
#include "defines.h"
#include "jni.h"
-#include "suggest/core/dictionary/bigram_dictionary.h"
#include "suggest/core/dictionary/property/word_property.h"
#include "suggest/core/policy/dictionary_header_structure_policy.h"
#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
@@ -75,7 +74,7 @@ class Dictionary {
int getMaxProbabilityOfExactMatches(const int *word, int length) const;
- int getBigramProbability(const PrevWordsInfo *const prevWordsInfo,
+ int getNgramProbability(const PrevWordsInfo *const prevWordsInfo,
const int *word, int length) const;
bool addUnigramEntry(const int *const codePoints, const int codePointCount,
@@ -119,7 +118,6 @@ class Dictionary {
const DictionaryStructureWithBufferPolicy::StructurePolicyPtr
mDictionaryStructureWithBufferPolicy;
- const BigramDictionary mBigramDictionary;
const SuggestInterfacePtr mGestureSuggest;
const SuggestInterfacePtr mTypingSuggest;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
index 5c62b9caf..002593c49 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
@@ -268,6 +268,10 @@ int PatriciaTriePolicy::getTerminalPtNodePositionOfWord(const int *const inWord,
int PatriciaTriePolicy::getProbability(const int unigramProbability,
const int bigramProbability) const {
+ // Due to space constraints, the probability for bigrams is approximate - the lower the unigram
+ // probability, the worse the precision. The theoritical maximum error in resulting probability
+ // is 8 - although in the practice it's never bigger than 3 or 4 in very bad cases. This means
+ // that sometimes, we'll see some bigrams interverted here, but it can't get too bad.
if (unigramProbability == NOT_A_PROBABILITY) {
return NOT_A_PROBABILITY;
} else if (bigramProbability == NOT_A_PROBABILITY) {
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index 28cce834c..ae184268c 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -20,6 +20,7 @@ import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Pair;
+import com.android.inputmethod.latin.PrevWordsInfo.WordInfo;
import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
import com.android.inputmethod.latin.makedict.CodePointUtils;
import com.android.inputmethod.latin.makedict.DictDecoder;
@@ -77,13 +78,13 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase {
private void addBigramWords(final BinaryDictionary binaryDictionary, final String word0,
final String word1, final int probability) {
- binaryDictionary.addNgramEntry(new PrevWordsInfo(word0), word1, probability,
+ binaryDictionary.addNgramEntry(new PrevWordsInfo(new WordInfo(word0)), word1, probability,
mCurrentTime /* timestamp */);
}
private static boolean isValidBigram(final BinaryDictionary binaryDictionary,
final String word0, final String word1) {
- return binaryDictionary.isValidNgram(new PrevWordsInfo(word0), word1);
+ return binaryDictionary.isValidNgram(new PrevWordsInfo(new WordInfo(word0)), word1);
}
private void forcePassingShortTime(final BinaryDictionary binaryDictionary) {
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
index 83ea19399..6ba18d665 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
@@ -21,6 +21,7 @@ import android.test.suitebuilder.annotation.LargeTest;
import android.text.TextUtils;
import android.util.Pair;
+import com.android.inputmethod.latin.PrevWordsInfo.WordInfo;
import com.android.inputmethod.latin.makedict.CodePointUtils;
import com.android.inputmethod.latin.makedict.FormatSpec;
import com.android.inputmethod.latin.makedict.WeightedString;
@@ -203,23 +204,23 @@ public class BinaryDictionaryTests extends AndroidTestCase {
private static void addBigramWords(final BinaryDictionary binaryDictionary, final String word0,
final String word1, final int probability) {
- binaryDictionary.addNgramEntry(new PrevWordsInfo(word0), word1, probability,
+ binaryDictionary.addNgramEntry(new PrevWordsInfo(new WordInfo(word0)), word1, probability,
BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */);
}
private static boolean isValidBigram(final BinaryDictionary binaryDictionary,
final String word0, final String word1) {
- return binaryDictionary.isValidNgram(new PrevWordsInfo(word0), word1);
+ return binaryDictionary.isValidNgram(new PrevWordsInfo(new WordInfo(word0)), word1);
}
private static void removeBigramEntry(final BinaryDictionary binaryDictionary,
final String word0, final String word1) {
- binaryDictionary.removeNgramEntry(new PrevWordsInfo(word0), word1);
+ binaryDictionary.removeNgramEntry(new PrevWordsInfo(new WordInfo(word0)), word1);
}
private static int getBigramProbability(final BinaryDictionary binaryDictionary,
final String word0, final String word1) {
- return binaryDictionary.getNgramProbability(new PrevWordsInfo(word0), word1);
+ return binaryDictionary.getNgramProbability(new PrevWordsInfo(new WordInfo(word0)), word1);
}
public void testAddUnigramWord() {
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index 2709ecba6..0552c221e 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -517,15 +517,21 @@ public class InputLogicTests extends InputTestsBase {
suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
}
- public void testNoPredictionsAfterPeriod() {
+ public void testPredictionsAfterPeriod() {
mLatinIME.clearPersonalizedDictionariesForTest();
final String WORD_TO_TYPE = "Barack. ";
type(WORD_TO_TYPE);
sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
runMessages();
- // Test the first prediction is not displayed
- final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
- assertEquals("no prediction after period", 0, suggestedWords.size());
+ SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
+ assertEquals("No prediction after period after inputting once.", 0, suggestedWords.size());
+
+ type(WORD_TO_TYPE);
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ suggestedWords = mLatinIME.getSuggestedWordsForTest();
+ assertEquals("Beginning-of-Sentence prediction after inputting 2 times.", "Barack",
+ suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
}
public void testPredictionsAfterRecorrection() {
diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
index 2c92bb3d6..5a5ec6d2b 100644
--- a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
+++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
@@ -156,16 +156,16 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
public void testGetPreviousWord() {
// If one of the following cases breaks, the bigram suggestions won't work.
assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
- "abc def", mSpacingAndPunctuations, 2).mPrevWord, "abc");
+ "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc");
assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE);
assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc. def", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE);
assertFalse(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
- "abc def", mSpacingAndPunctuations, 2).mIsBeginningOfSentence);
+ "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mIsBeginningOfSentence);
assertTrue(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
- "abc", mSpacingAndPunctuations, 2).mIsBeginningOfSentence);
+ "abc", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mIsBeginningOfSentence);
// The following tests reflect the current behavior of the function
// RichInputConnection#getNthPreviousWord.
// TODO: However at this time, the code does never go
@@ -174,20 +174,20 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
// logical. These tests are just there to catch any unintentional
// changes in the behavior of the RichInputConnection#getPreviousWord method.
assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
- "abc def ", mSpacingAndPunctuations, 2).mPrevWord, "abc");
+ "abc def ", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc");
assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
- "abc def.", mSpacingAndPunctuations, 2).mPrevWord, "abc");
+ "abc def.", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc");
assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
- "abc def .", mSpacingAndPunctuations, 2).mPrevWord, "def");
+ "abc def .", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "def");
assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc ", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE);
assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
- "abc def", mSpacingAndPunctuations, 1).mPrevWord, "def");
+ "abc def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def");
assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
- "abc def ", mSpacingAndPunctuations, 1).mPrevWord, "def");
+ "abc def ", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def");
assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
- "abc 'def", mSpacingAndPunctuations, 1).mPrevWord, "'def");
+ "abc 'def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "'def");
assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc def.", mSpacingAndPunctuations, 1), PrevWordsInfo.BEGINNING_OF_SENTENCE);
assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
index 8f32e5336..76eaef431 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -104,7 +104,8 @@ public class Ver4DictEncoder implements DictEncoder {
for (final WordProperty word0Property : dict) {
if (null == word0Property.mBigrams) continue;
for (final WeightedString word1 : word0Property.mBigrams) {
- final PrevWordsInfo prevWordsInfo = new PrevWordsInfo(word0Property.mWord);
+ final PrevWordsInfo prevWordsInfo =
+ new PrevWordsInfo(new PrevWordsInfo.WordInfo(word0Property.mWord));
if (!binaryDict.addNgramEntry(prevWordsInfo, word1.mWord,
word1.getProbability(), 0 /* timestamp */)) {
MakedictLog.e("Cannot add n-gram entry for "
diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
index 48d3a1cad..f87f3b494 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
@@ -22,6 +22,7 @@ import android.util.Log;
import com.android.inputmethod.latin.ExpandableBinaryDictionary;
import com.android.inputmethod.latin.PrevWordsInfo;
+import com.android.inputmethod.latin.PrevWordsInfo.WordInfo;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
import com.android.inputmethod.latin.utils.DistracterFilter;
import com.android.inputmethod.latin.utils.FileUtils;
@@ -115,7 +116,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true,
(int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()),
DistracterFilter.EMPTY_DISTRACTER_FILTER);
- prevWordsInfo = new PrevWordsInfo(word);
+ prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(new WordInfo(word));
}
}
@@ -262,11 +263,11 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
final UserHistoryDictionary dict =
PersonalizationHelper.getUserHistoryDictionary(getContext(), dummyLocale);
dict.waitAllTasksForTests();
- PrevWordsInfo prevWordsInfo = new PrevWordsInfo(null);
+ PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
for (final String word : words) {
UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, mCurrentTime,
DistracterFilter.EMPTY_DISTRACTER_FILTER);
- prevWordsInfo = new PrevWordsInfo(word);
+ prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(new WordInfo(word));
dict.waitAllTasksForTests();
assertTrue(dict.isInDictionary(word));
}