aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
-rw-r--r--java/src/com/android/inputmethod/latin/AutoCorrection.java11
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java49
-rw-r--r--java/src/com/android/inputmethod/latin/DicTraverseSession.java73
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java25
-rw-r--r--java/src/com/android/inputmethod/latin/NativeUtils.java32
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java54
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java2
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java4
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java30
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java1
10 files changed, 171 insertions, 110 deletions
diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java
index a66337404..048166807 100644
--- a/java/src/com/android/inputmethod/latin/AutoCorrection.java
+++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java
@@ -75,17 +75,10 @@ public class AutoCorrection {
return maxFreq;
}
- // Returns true if this is a whitelist entry, or it isn't in any dictionary.
- public static boolean isWhitelistedOrNotAWord(
+ // Returns true if this isn't in any dictionary.
+ public static boolean isNotAWord(
final ConcurrentHashMap<String, Dictionary> dictionaries,
final CharSequence word, final boolean ignoreCase) {
- final WhitelistDictionary whitelistDictionary =
- (WhitelistDictionary)dictionaries.get(Dictionary.TYPE_WHITELIST);
- // If "word" is in the whitelist dictionary, it should not be auto corrected.
- if (whitelistDictionary != null
- && whitelistDictionary.shouldForciblyAutoCorrectFrom(word)) {
- return true;
- }
return !isValidWord(dictionaries, word, ignoreCase);
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 534cffb2d..f0f5cd320 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -51,13 +51,15 @@ public class BinaryDictionary extends Dictionary {
private static final int TYPED_LETTER_MULTIPLIER = 2;
private long mNativeDict;
- private final int[] mInputCodes = new int[MAX_WORD_LENGTH];
+ private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH];
+ // TODO: The below should be int[] mOutputCodePoints
private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_RESULTS];
private final int[] mSpaceIndices = new int[MAX_SPACES];
private final int[] mOutputScores = new int[MAX_RESULTS];
private final int[] mOutputTypes = new int[MAX_RESULTS];
private final boolean mUseFullEditDistance;
+ private final DicTraverseSession mDicTraverseSession;
/**
* Constructor for the binary dictionary. This is supposed to be called from the
@@ -76,6 +78,8 @@ public class BinaryDictionary extends Dictionary {
super(dictType);
mUseFullEditDistance = useFullEditDistance;
loadDictionary(filename, offset, length);
+ mDicTraverseSession = new DicTraverseSession(locale);
+ mDicTraverseSession.initSession(mNativeDict);
}
static {
@@ -86,18 +90,17 @@ public class BinaryDictionary extends Dictionary {
int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords,
int maxPredictions);
private native void closeNative(long dict);
- private native int getFrequencyNative(long dict, int[] word, int wordLength);
+ private native int getFrequencyNative(long dict, int[] word);
private native boolean isValidBigramNative(long dict, int[] word1, int[] word2);
- private native int getSuggestionsNative(long dict, long proximityInfo, int[] xCoordinates,
- int[] yCoordinates, int[] times, int[] pointerIds, int[] inputCodes, int codesSize,
- int commitPoint, boolean isGesture,
+ private native int getSuggestionsNative(long dict, long proximityInfo, long traverseSession,
+ int[] xCoordinates, int[] yCoordinates, int[] times, int[] pointerIds,
+ int[] inputCodePoints, int codesSize, int commitPoint, boolean isGesture,
int[] prevWordCodePointArray, boolean useFullEditDistance, char[] outputChars,
int[] outputScores, int[] outputIndices, int[] outputTypes);
- private static native float calcNormalizedScoreNative(
- char[] before, int beforeLength, char[] after, int afterLength, int score);
- private static native int editDistanceNative(
- char[] before, int beforeLength, char[] after, int afterLength);
+ private static native float calcNormalizedScoreNative(char[] before, char[] after, int score);
+ private static native int editDistanceNative(char[] before, char[] after);
+ // TODO: Move native dict into session
private final void loadDictionary(String path, long startOffset, long length) {
mNativeDict = openNative(path, startOffset, length, TYPED_LETTER_MULTIPLIER,
FULL_WORD_SCORE_MULTIPLIER, MAX_WORD_LENGTH, MAX_WORDS, MAX_PREDICTIONS);
@@ -107,9 +110,7 @@ public class BinaryDictionary extends Dictionary {
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
final CharSequence prevWord, final ProximityInfo proximityInfo) {
if (!isValidDictionary()) return null;
- Arrays.fill(mInputCodes, WordComposer.NOT_A_CODE);
- Arrays.fill(mOutputChars, (char) 0);
- Arrays.fill(mOutputScores, 0);
+ Arrays.fill(mInputCodePoints, WordComposer.NOT_A_CODE);
// TODO: toLowerCase in the native code
final int[] prevWordCodePointArray = (null == prevWord)
? null : StringUtils.toCodePointArray(prevWord.toString());
@@ -119,7 +120,7 @@ public class BinaryDictionary extends Dictionary {
if (composerSize <= 1 || !isGesture) {
if (composerSize > MAX_WORD_LENGTH - 1) return null;
for (int i = 0; i < composerSize; i++) {
- mInputCodes[i] = composer.getCodeAt(i);
+ mInputCodePoints[i] = composer.getCodeAt(i);
}
}
@@ -127,9 +128,9 @@ public class BinaryDictionary extends Dictionary {
final int codesSize = isGesture ? ips.getPointerSize() : composerSize;
// proximityInfo and/or prevWordForBigrams may not be null.
final int tmpCount = getSuggestionsNative(mNativeDict,
- proximityInfo.getNativeProximityInfo(), ips.getXCoordinates(),
- ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(),
- mInputCodes, codesSize, 0 /* commitPoint */, isGesture, prevWordCodePointArray,
+ proximityInfo.getNativeProximityInfo(), mDicTraverseSession.getSession(),
+ ips.getXCoordinates(), ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(),
+ mInputCodePoints, codesSize, 0 /* commitPoint */, isGesture, prevWordCodePointArray,
mUseFullEditDistance, mOutputChars, mOutputScores, mSpaceIndices, mOutputTypes);
final int count = Math.min(tmpCount, MAX_PREDICTIONS);
@@ -142,9 +143,10 @@ public class BinaryDictionary extends Dictionary {
++len;
}
if (len > 0) {
+ final int score = SuggestedWordInfo.KIND_WHITELIST == mOutputTypes[j]
+ ? SuggestedWordInfo.MAX_SCORE : mOutputScores[j];
suggestions.add(new SuggestedWordInfo(
- new String(mOutputChars, start, len),
- mOutputScores[j], SuggestedWordInfo.KIND_CORRECTION, mDictType));
+ new String(mOutputChars, start, len), score, mOutputTypes[j], mDictType));
}
}
return suggestions;
@@ -155,13 +157,11 @@ public class BinaryDictionary extends Dictionary {
}
public static float calcNormalizedScore(String before, String after, int score) {
- return calcNormalizedScoreNative(before.toCharArray(), before.length(),
- after.toCharArray(), after.length(), score);
+ return calcNormalizedScoreNative(before.toCharArray(), after.toCharArray(), score);
}
public static int editDistance(String before, String after) {
- return editDistanceNative(
- before.toCharArray(), before.length(), after.toCharArray(), after.length());
+ return editDistanceNative(before.toCharArray(), after.toCharArray());
}
@Override
@@ -172,8 +172,8 @@ public class BinaryDictionary extends Dictionary {
@Override
public int getFrequency(CharSequence word) {
if (word == null) return -1;
- int[] chars = StringUtils.toCodePointArray(word.toString());
- return getFrequencyNative(mNativeDict, chars, chars.length);
+ int[] codePoints = StringUtils.toCodePointArray(word.toString());
+ return getFrequencyNative(mNativeDict, codePoints);
}
// TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni
@@ -187,6 +187,7 @@ public class BinaryDictionary extends Dictionary {
@Override
public synchronized void close() {
+ mDicTraverseSession.close();
closeInternal();
}
diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
new file mode 100644
index 000000000..c76815363
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import java.util.Locale;
+
+public class DicTraverseSession {
+ static {
+ JniUtils.loadNativeLibrary();
+ }
+ private native long setDicTraverseSessionNative(String locale);
+ private native void initDicTraverseSessionNative(long nativeDicTraverseSession,
+ long dictionary, int[] previousWord, int previousWordLength);
+ private native void releaseDicTraverseSessionNative(long nativeDicTraverseSession);
+
+ private long mNativeDicTraverseSession;
+
+ public DicTraverseSession(Locale locale) {
+ mNativeDicTraverseSession = createNativeDicTraverseSession(
+ locale != null ? locale.toString() : "");
+ }
+
+ public long getSession() {
+ return mNativeDicTraverseSession;
+ }
+
+ public void initSession(long dictionary) {
+ initSession(dictionary, null, 0);
+ }
+
+ public void initSession(long dictionary, int[] previousWord, int previousWordLength) {
+ initDicTraverseSessionNative(
+ mNativeDicTraverseSession, dictionary, previousWord, previousWordLength);
+ }
+
+ private final long createNativeDicTraverseSession(String locale) {
+ return setDicTraverseSessionNative(locale);
+ }
+
+ private void closeInternal() {
+ if (mNativeDicTraverseSession != 0) {
+ releaseDicTraverseSessionNative(mNativeDicTraverseSession);
+ mNativeDicTraverseSession = 0;
+ }
+ }
+
+ public void close() {
+ closeInternal();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ closeInternal();
+ } finally {
+ super.finalize();
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 25cddb22d..4a7d0403b 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -433,10 +433,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
}
+ // Note that this method is called from a non-UI thread.
@Override
public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable) {
mIsMainDictionaryAvailable = isMainDictionaryAvailable;
- updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability();
+ final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView != null) {
+ mainKeyboardView.setMainDictionaryAvailability(isMainDictionaryAvailable);
+ }
}
private void initSuggest() {
@@ -517,7 +521,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
/* package private */ void resetSuggestMainDict() {
final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
- mSuggest.resetMainDict(this, subtypeLocale);
+ mSuggest.resetMainDict(this, subtypeLocale, this /* SuggestInitializationListener */);
mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
}
@@ -701,7 +705,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
switcher.loadKeyboard(editorInfo, mCurrentSettings);
- updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability();
}
setSuggestionStripShownInternal(
isSuggestionsStripVisible(), /* needsInputViewShown */ false);
@@ -719,8 +722,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mHandler.cancelUpdateSuggestionStrip();
mHandler.cancelDoubleSpacesTimer();
+ mainKeyboardView.setMainDictionaryAvailability(mIsMainDictionaryAvailable);
mainKeyboardView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn,
mCurrentSettings.mKeyPreviewPopupDismissDelay);
+ mainKeyboardView.setGestureHandlingEnabledByUser(mCurrentSettings.mGestureInputEnabled);
+ mainKeyboardView.setGesturePreviewMode(mCurrentSettings.mGesturePreviewTrailEnabled,
+ mCurrentSettings.mGestureFloatingPreviewTextEnabled);
if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
}
@@ -2103,7 +2110,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mKeyboardSwitcher.getMainKeyboardView() != null) {
// Reload keyboard because the current language has been changed.
mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mCurrentSettings);
- updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability();
}
// Since we just changed languages, we should re-evaluate suggestions with whatever word
// we are currently composing. If we are not composing anything, we may want to display
@@ -2111,17 +2117,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mHandler.postUpdateSuggestionStrip();
}
- private void updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability() {
- final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
- if (mainKeyboardView != null) {
- final boolean shouldHandleGesture = mCurrentSettings.mGestureInputEnabled
- && mIsMainDictionaryAvailable;
- mainKeyboardView.setGestureHandlingMode(shouldHandleGesture,
- mCurrentSettings.mGesturePreviewTrailEnabled,
- mCurrentSettings.mGestureFloatingPreviewTextEnabled);
- }
- }
-
// TODO: Remove this method from {@link LatinIME} and move {@link FeedbackManager} to
// {@link KeyboardSwitcher}. Called from KeyboardSwitcher
public void hapticAndAudioFeedback(final int primaryCode) {
diff --git a/java/src/com/android/inputmethod/latin/NativeUtils.java b/java/src/com/android/inputmethod/latin/NativeUtils.java
deleted file mode 100644
index 9cc2bc02e..000000000
--- a/java/src/com/android/inputmethod/latin/NativeUtils.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-public class NativeUtils {
- static {
- JniUtils.loadNativeLibrary();
- }
-
- private NativeUtils() {
- // This utility class is not publicly instantiable.
- }
-
- /**
- * This method just calls up libm's powf() directly.
- */
- public static native float powf(float x, float y);
-}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 5e2a04124..8a2341d5e 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -60,13 +60,11 @@ public class Suggest {
// Locale used for upper- and title-casing words
private final Locale mLocale;
- private final SuggestInitializationListener mListener;
public Suggest(final Context context, final Locale locale,
final SuggestInitializationListener listener) {
- initAsynchronously(context, locale);
+ initAsynchronously(context, locale, listener);
mLocale = locale;
- mListener = listener;
}
/* package for test */ Suggest(final Context context, final File dictionary,
@@ -74,7 +72,6 @@ public class Suggest {
final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(context, dictionary,
startOffset, length /* useFullEditDistance */, false, locale);
mLocale = locale;
- mListener = null;
mMainDictionary = mainDict;
addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, mainDict);
initWhitelistAndAutocorrectAndPool(context, locale);
@@ -85,8 +82,9 @@ public class Suggest {
addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_WHITELIST, mWhiteListDictionary);
}
- private void initAsynchronously(final Context context, final Locale locale) {
- resetMainDict(context, locale);
+ private void initAsynchronously(final Context context, final Locale locale,
+ final SuggestInitializationListener listener) {
+ resetMainDict(context, locale, listener);
// TODO: read the whitelist and init the pool asynchronously too.
// initPool should be done asynchronously now that the pool is thread-safe.
@@ -104,10 +102,11 @@ public class Suggest {
}
}
- public void resetMainDict(final Context context, final Locale locale) {
+ public void resetMainDict(final Context context, final Locale locale,
+ final SuggestInitializationListener listener) {
mMainDictionary = null;
- if (mListener != null) {
- mListener.onUpdateMainDictionaryAvailability(hasMainDictionary());
+ if (listener != null) {
+ listener.onUpdateMainDictionaryAvailability(hasMainDictionary());
}
new Thread("InitializeBinaryDictionary") {
@Override
@@ -116,8 +115,8 @@ public class Suggest {
DictionaryFactory.createMainDictionaryFromManager(context, locale);
addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, newMainDict);
mMainDictionary = newMainDict;
- if (mListener != null) {
- mListener.onUpdateMainDictionaryAvailability(hasMainDictionary());
+ if (listener != null) {
+ listener.onUpdateMainDictionaryAvailability(hasMainDictionary());
}
}
}.start();
@@ -209,23 +208,34 @@ public class Suggest {
wordComposerForLookup, prevWordForBigram, proximityInfo));
}
- // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
- // but still autocorrected from - in the case the whitelist only capitalizes the word.
- // The whitelist should be case-insensitive, so it's not possible to be consistent with
- // a boolean flag. Right now this is handled with a slight hack in
- // WhitelistDictionary#shouldForciblyAutoCorrectFrom.
- final boolean allowsToBeAutoCorrected = AutoCorrection.isWhitelistedOrNotAWord(
- mDictionaries, consideredWord, wordComposer.isFirstCharCapitalized());
-
- final CharSequence whitelistedWord =
+ final CharSequence whitelistedWordFromWhitelistDictionary =
mWhiteListDictionary.getWhitelistedWord(consideredWord);
- if (whitelistedWord != null) {
+ if (whitelistedWordFromWhitelistDictionary != null) {
// MAX_SCORE ensures this will be considered strong enough to be auto-corrected
- suggestionsSet.add(new SuggestedWordInfo(whitelistedWord,
+ suggestionsSet.add(new SuggestedWordInfo(whitelistedWordFromWhitelistDictionary,
SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_WHITELIST,
Dictionary.TYPE_WHITELIST));
}
+ final CharSequence whitelistedWord;
+ if (suggestionsSet.isEmpty()) {
+ whitelistedWord = null;
+ } else if (SuggestedWordInfo.KIND_WHITELIST != suggestionsSet.first().mKind) {
+ whitelistedWord = null;
+ } else {
+ whitelistedWord = suggestionsSet.first().mWord;
+ }
+
+ // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
+ // but still autocorrected from - in the case the whitelist only capitalizes the word.
+ // The whitelist should be case-insensitive, so it's not possible to be consistent with
+ // a boolean flag. Right now this is handled with a slight hack in
+ // WhitelistDictionary#shouldForciblyAutoCorrectFrom.
+ final boolean allowsToBeAutoCorrected = (null != whitelistedWord
+ && !whitelistedWord.equals(consideredWord))
+ || AutoCorrection.isNotAWord(mDictionaries, consideredWord,
+ wordComposer.isFirstCharCapitalized());
+
final boolean hasAutoCorrection;
// TODO: using isCorrectionEnabled here is not very good. It's probably useless, because
// any attempt to do auto-correction is already shielded with a test for this flag; at the
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java
index 1de95d7b8..5a2fdf48e 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java
@@ -212,7 +212,7 @@ public class UserHistoryForgettingCurveUtils {
for (int j = 0; j < ELAPSED_TIME_MAX; ++j) {
final float elapsedHours = j * ELAPSED_TIME_INTERVAL_HOURS;
final float freq = initialFreq
- * NativeUtils.powf(initialFreq, elapsedHours / HALF_LIFE_HOURS);
+ * (float)Math.pow(initialFreq, elapsedHours / HALF_LIFE_HOURS);
final int intFreq = Math.min(FC_FREQ_MAX, Math.max(0, (int)freq));
SCORE_TABLE[i][j] = intFreq;
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 0171dc06d..06f5db749 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -193,7 +193,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
if (shouldFilterOut(inText, mScript)) {
DictAndProximity dictInfo = null;
try {
- dictInfo = mDictionaryPool.takeOrGetNull();
+ dictInfo = mDictionaryPool.pollWithDefaultTimeout();
if (null == dictInfo) {
return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
}
@@ -236,7 +236,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
boolean isInDict = true;
DictAndProximity dictInfo = null;
try {
- dictInfo = mDictionaryPool.takeOrGetNull();
+ dictInfo = mDictionaryPool.pollWithDefaultTimeout();
if (null == dictInfo) {
return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index 8fc632ee7..83f82faeb 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -16,14 +16,24 @@
package com.android.inputmethod.latin.spellcheck;
+import android.util.Log;
+
import java.util.Locale;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
/**
* A blocking queue that creates dictionaries up to a certain limit as necessary.
+ * As a deadlock-detecting device, if waiting for more than TIMEOUT = 3 seconds, we
+ * will clear the queue and generate its contents again. This is transparent for
+ * the client code, but may help with sloppy clients.
*/
@SuppressWarnings("serial")
public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
+ private final static String TAG = DictionaryPool.class.getSimpleName();
+ // How many seconds we wait for a dictionary to become available. Past this delay, we give up in
+ // fear some bug caused a deadlock, and reset the whole pool.
+ private final static int TIMEOUT = 3;
private final AndroidSpellCheckerService mService;
private final int mMaxSize;
private final Locale mLocale;
@@ -41,13 +51,23 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
}
@Override
- public DictAndProximity take() throws InterruptedException {
+ public DictAndProximity poll(final long timeout, final TimeUnit unit)
+ throws InterruptedException {
final DictAndProximity dict = poll();
if (null != dict) return dict;
synchronized(this) {
if (mSize >= mMaxSize) {
- // Our pool is already full. Wait until some dictionary is ready.
- return super.take();
+ // Our pool is already full. Wait until some dictionary is ready, or TIMEOUT
+ // expires to avoid a deadlock.
+ final DictAndProximity result = super.poll(timeout, unit);
+ if (null == result) {
+ Log.e(TAG, "Deadlock detected ! Resetting dictionary pool");
+ clear();
+ mSize = 1;
+ return mService.createDictAndProximity(mLocale);
+ } else {
+ return result;
+ }
} else {
++mSize;
return mService.createDictAndProximity(mLocale);
@@ -56,9 +76,9 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
}
// Convenience method
- public DictAndProximity takeOrGetNull() {
+ public DictAndProximity pollWithDefaultTimeout() {
try {
- return take();
+ return poll(TIMEOUT, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return null;
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
index 0103e8423..bd92d883b 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
@@ -111,6 +111,7 @@ public class SpellCheckerProximityInfo {
NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
};
static {
buildProximityIndices(PROXIMITY, INDICES);