[ML7] Have multiple DictionaryGroup instances in facilitator

This is the central change of multilingual input.

Bug: 11230254
Change-Id: Id8b68fb101e837e8cf182ab4bc1e55e4da5cc49d
main
Jean Chalard 2014-09-11 18:51:31 +09:00
parent 97b465044f
commit 8cd5326622
7 changed files with 187 additions and 111 deletions

View File

@ -61,9 +61,9 @@ public class DictionaryFacilitator {
// dictionary. // dictionary.
private static final int CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT = 140; private static final int CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT = 140;
private DictionaryGroup mDictionaryGroup = new DictionaryGroup(); private DictionaryGroup[] mDictionaryGroups = new DictionaryGroup[] { new DictionaryGroup() };
private boolean mIsUserDictEnabled = false; private boolean mIsUserDictEnabled = false;
private volatile CountDownLatch mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0); private volatile CountDownLatch mLatchForWaitingLoadingMainDictionaries = new CountDownLatch(0);
// To synchronize assigning mDictionaryGroup to ensure closing dictionaries. // To synchronize assigning mDictionaryGroup to ensure closing dictionaries.
private final Object mLock = new Object(); private final Object mLock = new Object();
private final DistracterFilter mDistracterFilter; private final DistracterFilter mDistracterFilter;
@ -193,8 +193,9 @@ public class DictionaryFacilitator {
mPersonalizationHelper.setIsMonolingualUser(isMonolingualUser); mPersonalizationHelper.setIsMonolingualUser(isMonolingualUser);
} }
// TODO: remove this, replace with version returning multiple locales
public Locale getLocale() { public Locale getLocale() {
return mDictionaryGroup.mLocale; return mDictionaryGroups[0].mLocale;
} }
private static ExpandableBinaryDictionary getSubDict(final String dictType, private static ExpandableBinaryDictionary getSubDict(final String dictType,
@ -226,6 +227,21 @@ public class DictionaryFacilitator {
usePersonalizedDicts, forceReloadMainDictionary, listener, "" /* dictNamePrefix */); usePersonalizedDicts, forceReloadMainDictionary, listener, "" /* dictNamePrefix */);
} }
private DictionaryGroup findDictionaryGroupWithLocale(final DictionaryGroup[] dictionaryGroups,
final Locale locale) {
for (int i = 0; i < dictionaryGroups.length; ++i) {
if (locale.equals(dictionaryGroups[i].mLocale)) {
return dictionaryGroups[i];
}
}
return null;
}
private DictionaryGroup getDictionaryGroupForActiveLanguage() {
// TODO: implement this
return mDictionaryGroups[0];
}
public void resetDictionariesWithDictNamePrefix(final Context context, public void resetDictionariesWithDictNamePrefix(final Context context,
final Locale newLocaleToUse, final Locale newLocaleToUse,
final boolean useContactsDict, final boolean usePersonalizedDicts, final boolean useContactsDict, final boolean usePersonalizedDicts,
@ -252,7 +268,7 @@ public class DictionaryFacilitator {
final ArrayList<String> dictsForLocale = new ArrayList<>(); final ArrayList<String> dictsForLocale = new ArrayList<>();
existingDictsToCleanup.put(newLocale, dictsForLocale); existingDictsToCleanup.put(newLocale, dictsForLocale);
final DictionaryGroup currentDictionaryGroupForLocale = final DictionaryGroup currentDictionaryGroupForLocale =
newLocale.equals(mDictionaryGroup.mLocale) ? mDictionaryGroup : null; findDictionaryGroupWithLocale(mDictionaryGroups, newLocale);
if (null == currentDictionaryGroupForLocale) { if (null == currentDictionaryGroupForLocale) {
continue; continue;
} }
@ -266,10 +282,11 @@ public class DictionaryFacilitator {
} }
} }
final HashMap<Locale, DictionaryGroup> newDictionaryGroups = new HashMap<>(); final DictionaryGroup[] newDictionaryGroups = new DictionaryGroup[newLocales.length];
for (final Locale newLocale : newLocales) { for (int i = 0; i < newLocales.length; ++i) {
final Locale newLocale = newLocales[i];
final DictionaryGroup dictionaryGroupForLocale = final DictionaryGroup dictionaryGroupForLocale =
newLocale.equals(mDictionaryGroup.mLocale) ? mDictionaryGroup : null; findDictionaryGroupWithLocale(mDictionaryGroups, newLocale);
final ArrayList<String> dictsToCleanupForLocale = existingDictsToCleanup.get(newLocale); final ArrayList<String> dictsToCleanupForLocale = existingDictsToCleanup.get(newLocale);
final boolean noExistingDictsForThisLocale = (null == dictionaryGroupForLocale); final boolean noExistingDictsForThisLocale = (null == dictionaryGroupForLocale);
@ -297,30 +314,29 @@ public class DictionaryFacilitator {
} }
subDicts.put(subDictType, subDict); subDicts.put(subDictType, subDict);
} }
newDictionaryGroups.put(newLocale, new DictionaryGroup(newLocale, mainDict, subDicts)); newDictionaryGroups[i] = new DictionaryGroup(newLocale, mainDict, subDicts);
} }
// Replace Dictionaries. // Replace Dictionaries.
// TODO: use multiple locales. final DictionaryGroup[] oldDictionaryGroups;
final DictionaryGroup newDictionaryGroup = newDictionaryGroups.get(newLocaleToUse);
final DictionaryGroup oldDictionaryGroup;
synchronized (mLock) { synchronized (mLock) {
oldDictionaryGroup = mDictionaryGroup; oldDictionaryGroups = mDictionaryGroups;
mDictionaryGroup = newDictionaryGroup; mDictionaryGroups = newDictionaryGroups;
mIsUserDictEnabled = UserBinaryDictionary.isEnabled(context); mIsUserDictEnabled = UserBinaryDictionary.isEnabled(context);
if (null == newDictionaryGroup.getDict(Dictionary.TYPE_MAIN)) { if (hasAtLeastOneUninitializedMainDictionary()) {
asyncReloadUninitializedMainDictionaries(context, newLocales, listener); asyncReloadUninitializedMainDictionaries(context, newLocales, listener);
} }
} }
if (listener != null) { if (listener != null) {
listener.onUpdateMainDictionaryAvailability(hasInitializedMainDictionary()); listener.onUpdateMainDictionaryAvailability(hasAtLeastOneInitializedMainDictionary());
} }
// Clean up old dictionaries. // Clean up old dictionaries.
for (final Locale localeToCleanUp : existingDictsToCleanup.keySet()) { for (final Locale localeToCleanUp : existingDictsToCleanup.keySet()) {
final ArrayList<String> dictTypesToCleanUp = final ArrayList<String> dictTypesToCleanUp =
existingDictsToCleanup.get(localeToCleanUp); existingDictsToCleanup.get(localeToCleanUp);
final DictionaryGroup dictionarySetToCleanup = oldDictionaryGroup; final DictionaryGroup dictionarySetToCleanup =
findDictionaryGroupWithLocale(oldDictionaryGroups, localeToCleanUp);
for (final String dictType : dictTypesToCleanUp) { for (final String dictType : dictTypesToCleanUp) {
dictionarySetToCleanup.closeDict(dictType); dictionarySetToCleanup.closeDict(dictType);
} }
@ -330,12 +346,18 @@ public class DictionaryFacilitator {
private void asyncReloadUninitializedMainDictionaries(final Context context, private void asyncReloadUninitializedMainDictionaries(final Context context,
final Locale[] locales, final DictionaryInitializationListener listener) { final Locale[] locales, final DictionaryInitializationListener listener) {
final CountDownLatch latchForWaitingLoadingMainDictionary = new CountDownLatch(1); final CountDownLatch latchForWaitingLoadingMainDictionary = new CountDownLatch(1);
mLatchForWaitingLoadingMainDictionary = latchForWaitingLoadingMainDictionary; mLatchForWaitingLoadingMainDictionaries = latchForWaitingLoadingMainDictionary;
ExecutorUtils.getExecutor("InitializeBinaryDictionary").execute(new Runnable() { ExecutorUtils.getExecutor("InitializeBinaryDictionary").execute(new Runnable() {
@Override @Override
public void run() { public void run() {
for (final Locale locale : locales) { for (final Locale locale : locales) {
final DictionaryGroup dictionaryGroup = mDictionaryGroup; final DictionaryGroup dictionaryGroup =
findDictionaryGroupWithLocale(mDictionaryGroups, locale);
if (null == dictionaryGroup) {
// This should never happen, but better safe than crashy
Log.w(TAG, "Expected a dictionary group for " + locale + " but none found");
continue;
}
final Dictionary mainDict = final Dictionary mainDict =
DictionaryFactory.createMainDictionaryFromManager(context, locale); DictionaryFactory.createMainDictionaryFromManager(context, locale);
synchronized (mLock) { synchronized (mLock) {
@ -348,7 +370,8 @@ public class DictionaryFacilitator {
} }
} }
if (listener != null) { if (listener != null) {
listener.onUpdateMainDictionaryAvailability(hasInitializedMainDictionary()); listener.onUpdateMainDictionaryAvailability(
hasAtLeastOneInitializedMainDictionary());
} }
latchForWaitingLoadingMainDictionary.countDown(); latchForWaitingLoadingMainDictionary.countDown();
} }
@ -381,18 +404,21 @@ public class DictionaryFacilitator {
subDicts.put(dictType, dict); subDicts.put(dictType, dict);
} }
} }
mDictionaryGroup = new DictionaryGroup(locale, mainDictionary, subDicts); mDictionaryGroups = new DictionaryGroup[] {
new DictionaryGroup(locale, mainDictionary, subDicts) };
} }
public void closeDictionaries() { public void closeDictionaries() {
final DictionaryGroup dictionaryGroup; final DictionaryGroup[] dictionaryGroups;
synchronized (mLock) { synchronized (mLock) {
dictionaryGroup = mDictionaryGroup; dictionaryGroups = mDictionaryGroups;
mDictionaryGroup = new DictionaryGroup(); mDictionaryGroups = new DictionaryGroup[] { new DictionaryGroup() };
} }
for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
dictionaryGroup.closeDict(dictType); dictionaryGroup.closeDict(dictType);
} }
}
mDistracterFilter.close(); mDistracterFilter.close();
if (mPersonalizationHelper != null) { if (mPersonalizationHelper != null) {
mPersonalizationHelper.close(); mPersonalizationHelper.close();
@ -401,42 +427,73 @@ public class DictionaryFacilitator {
@UsedForTesting @UsedForTesting
public ExpandableBinaryDictionary getSubDictForTesting(final String dictName) { public ExpandableBinaryDictionary getSubDictForTesting(final String dictName) {
return mDictionaryGroup.getSubDict(dictName); return mDictionaryGroups[0].getSubDict(dictName);
} }
// The main dictionary could have been loaded asynchronously. Don't cache the return value // The main dictionaries are loaded asynchronously. Don't cache the return value
// of this method. // of these methods.
public boolean hasInitializedMainDictionary() { public boolean hasAtLeastOneInitializedMainDictionary() {
final Dictionary mainDict = mDictionaryGroup.getDict(Dictionary.TYPE_MAIN); final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
return mainDict != null && mainDict.isInitialized(); for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
final Dictionary mainDict = dictionaryGroup.getDict(Dictionary.TYPE_MAIN);
if (mainDict != null && mainDict.isInitialized()) {
return true;
}
}
return false;
}
public boolean hasAtLeastOneUninitializedMainDictionary() {
final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
final Dictionary mainDict = dictionaryGroup.getDict(Dictionary.TYPE_MAIN);
if (mainDict == null || !mainDict.isInitialized()) {
return true;
}
}
return false;
} }
public boolean hasPersonalizationDictionary() { public boolean hasPersonalizationDictionary() {
return mDictionaryGroup.hasDict(Dictionary.TYPE_PERSONALIZATION); final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
if (dictionaryGroup.hasDict(Dictionary.TYPE_PERSONALIZATION)) {
return true;
}
}
return false;
} }
public void flushPersonalizationDictionary() { public void flushPersonalizationDictionary() {
final HashSet<ExpandableBinaryDictionary> personalizationDictsUsedForSuggestion =
new HashSet<>();
final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
final ExpandableBinaryDictionary personalizationDictUsedForSuggestion = final ExpandableBinaryDictionary personalizationDictUsedForSuggestion =
mDictionaryGroup.getSubDict(Dictionary.TYPE_PERSONALIZATION); dictionaryGroup.getSubDict(Dictionary.TYPE_PERSONALIZATION);
personalizationDictsUsedForSuggestion.add(personalizationDictUsedForSuggestion);
}
mPersonalizationHelper.flushPersonalizationDictionariesToUpdate( mPersonalizationHelper.flushPersonalizationDictionariesToUpdate(
personalizationDictUsedForSuggestion); personalizationDictsUsedForSuggestion);
mDistracterFilter.close(); mDistracterFilter.close();
} }
public void waitForLoadingMainDictionary(final long timeout, final TimeUnit unit) public void waitForLoadingMainDictionaries(final long timeout, final TimeUnit unit)
throws InterruptedException { throws InterruptedException {
mLatchForWaitingLoadingMainDictionary.await(timeout, unit); mLatchForWaitingLoadingMainDictionaries.await(timeout, unit);
} }
@UsedForTesting @UsedForTesting
public void waitForLoadingDictionariesForTesting(final long timeout, final TimeUnit unit) public void waitForLoadingDictionariesForTesting(final long timeout, final TimeUnit unit)
throws InterruptedException { throws InterruptedException {
waitForLoadingMainDictionary(timeout, unit); waitForLoadingMainDictionaries(timeout, unit);
final Map<String, ExpandableBinaryDictionary> dictMap = mDictionaryGroup.mSubDictMap; final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
for (final ExpandableBinaryDictionary dict : dictMap.values()) { for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
for (final ExpandableBinaryDictionary dict : dictionaryGroup.mSubDictMap.values()) {
dict.waitAllTasksForTests(); dict.waitAllTasksForTests();
} }
} }
}
public boolean isUserDictionaryEnabled() { public boolean isUserDictionaryEnabled() {
return mIsUserDictEnabled; return mIsUserDictEnabled;
@ -453,7 +510,7 @@ public class DictionaryFacilitator {
public void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized, public void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized,
final PrevWordsInfo prevWordsInfo, final int timeStampInSeconds, final PrevWordsInfo prevWordsInfo, final int timeStampInSeconds,
final boolean blockPotentiallyOffensive) { final boolean blockPotentiallyOffensive) {
final DictionaryGroup dictionaryGroup = mDictionaryGroup; final DictionaryGroup dictionaryGroup = getDictionaryGroupForActiveLanguage();
final String[] words = suggestion.split(Constants.WORD_SEPARATOR); final String[] words = suggestion.split(Constants.WORD_SEPARATOR);
PrevWordsInfo prevWordsInfoForCurrentWord = prevWordsInfo; PrevWordsInfo prevWordsInfoForCurrentWord = prevWordsInfo;
for (int i = 0; i < words.length; i++) { for (int i = 0; i < words.length; i++) {
@ -520,7 +577,8 @@ public class DictionaryFacilitator {
} }
private void removeWord(final String dictName, final String word) { private void removeWord(final String dictName, final String word) {
final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictName); final ExpandableBinaryDictionary dictionary =
getDictionaryGroupForActiveLanguage().getSubDict(dictName);
if (dictionary != null) { if (dictionary != null) {
dictionary.removeUnigramEntryDynamically(word); dictionary.removeUnigramEntryDynamically(word);
} }
@ -536,10 +594,11 @@ public class DictionaryFacilitator {
public SuggestionResults getSuggestionResults(final WordComposer composer, public SuggestionResults getSuggestionResults(final WordComposer composer,
final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo,
final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) { final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) {
final DictionaryGroup dictionaryGroup = mDictionaryGroup; final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
final SuggestionResults suggestionResults = final SuggestionResults suggestionResults =
new SuggestionResults(SuggestedWords.MAX_SUGGESTIONS); new SuggestionResults(SuggestedWords.MAX_SUGGESTIONS);
final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT }; final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT };
for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
final Dictionary dictionary = dictionaryGroup.getDict(dictType); final Dictionary dictionary = dictionaryGroup.getDict(dictType);
if (null == dictionary) continue; if (null == dictionary) continue;
@ -552,6 +611,7 @@ public class DictionaryFacilitator {
suggestionResults.mRawSuggestions.addAll(dictionarySuggestions); suggestionResults.mRawSuggestions.addAll(dictionarySuggestions);
} }
} }
}
return suggestionResults; return suggestionResults;
} }
@ -559,9 +619,10 @@ public class DictionaryFacilitator {
if (TextUtils.isEmpty(word)) { if (TextUtils.isEmpty(word)) {
return false; return false;
} }
final DictionaryGroup dictionaryGroup = mDictionaryGroup; final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
if (dictionaryGroup.mLocale == null) { if (dictionaryGroup.mLocale == null) {
return false; continue;
} }
final String lowerCasedWord = word.toLowerCase(dictionaryGroup.mLocale); final String lowerCasedWord = word.toLowerCase(dictionaryGroup.mLocale);
for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
@ -575,6 +636,7 @@ public class DictionaryFacilitator {
return true; return true;
} }
} }
}
return false; return false;
} }
@ -584,7 +646,8 @@ public class DictionaryFacilitator {
return Dictionary.NOT_A_PROBABILITY; return Dictionary.NOT_A_PROBABILITY;
} }
int maxFreq = Dictionary.NOT_A_PROBABILITY; int maxFreq = Dictionary.NOT_A_PROBABILITY;
final DictionaryGroup dictionaryGroup = mDictionaryGroup; final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
final Dictionary dictionary = dictionaryGroup.getDict(dictType); final Dictionary dictionary = dictionaryGroup.getDict(dictType);
if (dictionary == null) continue; if (dictionary == null) continue;
@ -598,6 +661,7 @@ public class DictionaryFacilitator {
maxFreq = tempFreq; maxFreq = tempFreq;
} }
} }
}
return maxFreq; return maxFreq;
} }
@ -610,11 +674,14 @@ public class DictionaryFacilitator {
} }
private void clearSubDictionary(final String dictName) { private void clearSubDictionary(final String dictName) {
final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictName); final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
final ExpandableBinaryDictionary dictionary = dictionaryGroup.getSubDict(dictName);
if (dictionary != null) { if (dictionary != null) {
dictionary.clear(); dictionary.clear();
} }
} }
}
public void clearUserHistoryDictionary() { public void clearUserHistoryDictionary() {
clearSubDictionary(Dictionary.TYPE_USER_HISTORY); clearSubDictionary(Dictionary.TYPE_USER_HISTORY);
@ -641,8 +708,10 @@ public class DictionaryFacilitator {
public void addPhraseToContextualDictionary(final String[] phrase, final int probability, public void addPhraseToContextualDictionary(final String[] phrase, final int probability,
final int bigramProbabilityForWords, final int bigramProbabilityForPhrases) { final int bigramProbabilityForWords, final int bigramProbabilityForPhrases) {
// TODO: we're inserting the phrase into the dictionary for the active language. Rethink
// this a bit from a theoretical point of view.
final ExpandableBinaryDictionary contextualDict = final ExpandableBinaryDictionary contextualDict =
mDictionaryGroup.getSubDict(Dictionary.TYPE_CONTEXTUAL); getDictionaryGroupForActiveLanguage().getSubDict(Dictionary.TYPE_CONTEXTUAL);
if (contextualDict == null) { if (contextualDict == null) {
return; return;
} }
@ -675,7 +744,9 @@ public class DictionaryFacilitator {
} }
public void dumpDictionaryForDebug(final String dictName) { public void dumpDictionaryForDebug(final String dictName) {
final ExpandableBinaryDictionary dictToDump = mDictionaryGroup.getSubDict(dictName); final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
final ExpandableBinaryDictionary dictToDump = dictionaryGroup.getSubDict(dictName);
if (dictToDump == null) { if (dictToDump == null) {
Log.e(TAG, "Cannot dump " + dictName + ". " Log.e(TAG, "Cannot dump " + dictName + ". "
+ "The dictionary is not being used for suggestion or cannot be dumped."); + "The dictionary is not being used for suggestion or cannot be dumped.");
@ -683,15 +754,18 @@ public class DictionaryFacilitator {
} }
dictToDump.dumpAllWordsForDebug(); dictToDump.dumpAllWordsForDebug();
} }
}
public ArrayList<Pair<String, DictionaryStats>> getStatsOfEnabledSubDicts() { public ArrayList<Pair<String, DictionaryStats>> getStatsOfEnabledSubDicts() {
final ArrayList<Pair<String, DictionaryStats>> statsOfEnabledSubDicts = new ArrayList<>(); final ArrayList<Pair<String, DictionaryStats>> statsOfEnabledSubDicts = new ArrayList<>();
final DictionaryGroup dictionaryGroup = mDictionaryGroup; final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
for (final String dictType : SUB_DICT_TYPES) { for (final String dictType : SUB_DICT_TYPES) {
final ExpandableBinaryDictionary dictionary = dictionaryGroup.getSubDict(dictType); final ExpandableBinaryDictionary dictionary = dictionaryGroup.getSubDict(dictType);
if (dictionary == null) continue; if (dictionary == null) continue;
statsOfEnabledSubDicts.add(new Pair<>(dictType, dictionary.getDictionaryStats())); statsOfEnabledSubDicts.add(new Pair<>(dictType, dictionary.getDictionaryStats()));
} }
}
return statsOfEnabledSubDicts; return statsOfEnabledSubDicts;
} }
} }

View File

@ -84,7 +84,7 @@ public class DictionaryFacilitatorLruCache {
private void waitForLoadingMainDictionary(final DictionaryFacilitator dictionaryFacilitator) { private void waitForLoadingMainDictionary(final DictionaryFacilitator dictionaryFacilitator) {
for (int i = 0; i < MAX_RETRY_COUNT_FOR_WAITING_FOR_LOADING_DICT; i++) { for (int i = 0; i < MAX_RETRY_COUNT_FOR_WAITING_FOR_LOADING_DICT; i++) {
try { try {
dictionaryFacilitator.waitForLoadingMainDictionary( dictionaryFacilitator.waitForLoadingMainDictionaries(
WAIT_FOR_LOADING_MAIN_DICT_IN_MILLISECONDS, TimeUnit.MILLISECONDS); WAIT_FOR_LOADING_MAIN_DICT_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
return; return;
} catch (final InterruptedException e) { } catch (final InterruptedException e) {

View File

@ -156,23 +156,25 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
} }
private void asyncExecuteTaskWithWriteLock(final Runnable task) { private void asyncExecuteTaskWithWriteLock(final Runnable task) {
asyncExecuteTaskWithLock(mLock.writeLock(), task); asyncExecuteTaskWithLock(mLock.writeLock(), mDictName /* executorName */, task);
} }
private void asyncExecuteTaskWithLock(final Lock lock, final Runnable task) { private void asyncExecuteTaskWithLock(final Lock lock, final String executorName,
asyncPreCheckAndExecuteTaskWithLock(lock, null /* preCheckTask */, task); final Runnable task) {
asyncPreCheckAndExecuteTaskWithLock(lock, null /* preCheckTask */, executorName, task);
} }
private void asyncPreCheckAndExecuteTaskWithWriteLock( private void asyncPreCheckAndExecuteTaskWithWriteLock(
final Callable<Boolean> preCheckTask, final Runnable task) { final Callable<Boolean> preCheckTask, final Runnable task) {
asyncPreCheckAndExecuteTaskWithLock(mLock.writeLock(), preCheckTask, task); asyncPreCheckAndExecuteTaskWithLock(mLock.writeLock(), preCheckTask,
mDictName /* executorName */, task);
} }
// Execute task with lock when the result of preCheckTask is true or preCheckTask is null. // Execute task with lock when the result of preCheckTask is true or preCheckTask is null.
private void asyncPreCheckAndExecuteTaskWithLock(final Lock lock, private void asyncPreCheckAndExecuteTaskWithLock(final Lock lock,
final Callable<Boolean> preCheckTask, final Runnable task) { final Callable<Boolean> preCheckTask, final String executorName, final Runnable task) {
ExecutorUtils.getExecutor(mDictName).execute(new Runnable() { ExecutorUtils.getExecutor(executorName).execute(new Runnable() {
@Override @Override
public void run() { public void run() {
if (preCheckTask != null) { if (preCheckTask != null) {
@ -676,10 +678,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
public void dumpAllWordsForDebug() { public void dumpAllWordsForDebug() {
reloadDictionaryIfRequired(); reloadDictionaryIfRequired();
asyncExecuteTaskWithLock(mLock.readLock(), new Runnable() { asyncExecuteTaskWithLock(mLock.readLock(), "dumpAllWordsForDebug", new Runnable() {
@Override @Override
public void run() { public void run() {
Log.d(TAG, "Dump dictionary: " + mDictName); Log.d(TAG, "Dump dictionary: " + mDictName + " for " + mLocale);
try { try {
final DictionaryHeader header = mBinaryDictionary.getHeader(); final DictionaryHeader header = mBinaryDictionary.getHeader();
Log.d(TAG, "Format version: " + mBinaryDictionary.getFormatVersion()); Log.d(TAG, "Format version: " + mBinaryDictionary.getFormatVersion());

View File

@ -998,7 +998,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mHandler.cancelUpdateSuggestionStrip(); mHandler.cancelUpdateSuggestionStrip();
mainKeyboardView.setMainDictionaryAvailability( mainKeyboardView.setMainDictionaryAvailability(
mDictionaryFacilitator.hasInitializedMainDictionary()); mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary());
mainKeyboardView.setKeyPreviewPopupEnabled(currentSettingsValues.mKeyPreviewPopupOn, mainKeyboardView.setKeyPreviewPopupEnabled(currentSettingsValues.mKeyPreviewPopupOn,
currentSettingsValues.mKeyPreviewPopupDismissDelay); currentSettingsValues.mKeyPreviewPopupDismissDelay);
mainKeyboardView.setSlidingKeyInputPreviewEnabled( mainKeyboardView.setSlidingKeyInputPreviewEnabled(

View File

@ -88,17 +88,17 @@ public class PersonalizationHelperForDictionaryFacilitator {
/** /**
* Flush personalization dictionaries to dictionary files. Close dictionaries after writing * Flush personalization dictionaries to dictionary files. Close dictionaries after writing
* files except the dictionary that is used for generating suggestions. * files except the dictionaries that is used for generating suggestions.
* *
* @param personalizationDictUsedForSuggestion the personalization dictionary used for * @param personalizationDictsUsedForSuggestion the personalization dictionaries used for
* generating suggestions that won't be closed. * generating suggestions that won't be closed.
*/ */
public void flushPersonalizationDictionariesToUpdate( public void flushPersonalizationDictionariesToUpdate(
final ExpandableBinaryDictionary personalizationDictUsedForSuggestion) { final HashSet<ExpandableBinaryDictionary> personalizationDictsUsedForSuggestion) {
for (final ExpandableBinaryDictionary personalizationDict : for (final ExpandableBinaryDictionary personalizationDict :
mPersonalizationDictsToUpdate.values()) { mPersonalizationDictsToUpdate.values()) {
personalizationDict.asyncFlushBinaryDictionary(); personalizationDict.asyncFlushBinaryDictionary();
if (personalizationDict != personalizationDictUsedForSuggestion) { if (!personalizationDictsUsedForSuggestion.contains(personalizationDict)) {
// Close if the dictionary is not being used for suggestion. // Close if the dictionary is not being used for suggestion.
personalizationDict.close(); personalizationDict.close();
} }

View File

@ -157,7 +157,7 @@ public final class Suggest {
if (!isCorrectionEnabled || !allowsToBeAutoCorrected || resultsArePredictions if (!isCorrectionEnabled || !allowsToBeAutoCorrected || resultsArePredictions
|| suggestionResults.isEmpty() || wordComposer.hasDigits() || suggestionResults.isEmpty() || wordComposer.hasDigits()
|| wordComposer.isMostlyCaps() || wordComposer.isResumed() || wordComposer.isMostlyCaps() || wordComposer.isResumed()
|| !mDictionaryFacilitator.hasInitializedMainDictionary() || !mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary()
|| suggestionResults.first().isKindOf(SuggestedWordInfo.KIND_SHORTCUT)) { || suggestionResults.first().isKindOf(SuggestedWordInfo.KIND_SHORTCUT)) {
// If we don't have a main dictionary, we never want to auto-correct. The reason for // If we don't have a main dictionary, we never want to auto-correct. The reason for
// this is, the user may have a contact whose name happens to match a valid word in // this is, the user may have a contact whose name happens to match a valid word in

View File

@ -185,7 +185,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
try { try {
final DictionaryFacilitator dictionaryFacilitator = final DictionaryFacilitator dictionaryFacilitator =
mDictionaryFacilitatorCache.get(locale); mDictionaryFacilitatorCache.get(locale);
return dictionaryFacilitator.hasInitializedMainDictionary(); return dictionaryFacilitator.hasAtLeastOneInitializedMainDictionary();
} finally { } finally {
mSemaphore.release(); mSemaphore.release();
} }