Loading java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +169 −95 Original line number Diff line number Diff line Loading @@ -61,9 +61,9 @@ public class DictionaryFacilitator { // dictionary. 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 volatile CountDownLatch mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0); private volatile CountDownLatch mLatchForWaitingLoadingMainDictionaries = new CountDownLatch(0); // To synchronize assigning mDictionaryGroup to ensure closing dictionaries. private final Object mLock = new Object(); private final DistracterFilter mDistracterFilter; Loading Loading @@ -193,8 +193,9 @@ public class DictionaryFacilitator { mPersonalizationHelper.setIsMonolingualUser(isMonolingualUser); } // TODO: remove this, replace with version returning multiple locales public Locale getLocale() { return mDictionaryGroup.mLocale; return mDictionaryGroups[0].mLocale; } private static ExpandableBinaryDictionary getSubDict(final String dictType, Loading Loading @@ -226,6 +227,21 @@ public class DictionaryFacilitator { 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, final Locale newLocaleToUse, final boolean useContactsDict, final boolean usePersonalizedDicts, Loading @@ -252,7 +268,7 @@ public class DictionaryFacilitator { final ArrayList<String> dictsForLocale = new ArrayList<>(); existingDictsToCleanup.put(newLocale, dictsForLocale); final DictionaryGroup currentDictionaryGroupForLocale = newLocale.equals(mDictionaryGroup.mLocale) ? mDictionaryGroup : null; findDictionaryGroupWithLocale(mDictionaryGroups, newLocale); if (null == currentDictionaryGroupForLocale) { continue; } Loading @@ -266,10 +282,11 @@ public class DictionaryFacilitator { } } final HashMap<Locale, DictionaryGroup> newDictionaryGroups = new HashMap<>(); for (final Locale newLocale : newLocales) { final DictionaryGroup[] newDictionaryGroups = new DictionaryGroup[newLocales.length]; for (int i = 0; i < newLocales.length; ++i) { final Locale newLocale = newLocales[i]; final DictionaryGroup dictionaryGroupForLocale = newLocale.equals(mDictionaryGroup.mLocale) ? mDictionaryGroup : null; findDictionaryGroupWithLocale(mDictionaryGroups, newLocale); final ArrayList<String> dictsToCleanupForLocale = existingDictsToCleanup.get(newLocale); final boolean noExistingDictsForThisLocale = (null == dictionaryGroupForLocale); Loading Loading @@ -297,30 +314,29 @@ public class DictionaryFacilitator { } subDicts.put(subDictType, subDict); } newDictionaryGroups.put(newLocale, new DictionaryGroup(newLocale, mainDict, subDicts)); newDictionaryGroups[i] = new DictionaryGroup(newLocale, mainDict, subDicts); } // Replace Dictionaries. // TODO: use multiple locales. final DictionaryGroup newDictionaryGroup = newDictionaryGroups.get(newLocaleToUse); final DictionaryGroup oldDictionaryGroup; final DictionaryGroup[] oldDictionaryGroups; synchronized (mLock) { oldDictionaryGroup = mDictionaryGroup; mDictionaryGroup = newDictionaryGroup; oldDictionaryGroups = mDictionaryGroups; mDictionaryGroups = newDictionaryGroups; mIsUserDictEnabled = UserBinaryDictionary.isEnabled(context); if (null == newDictionaryGroup.getDict(Dictionary.TYPE_MAIN)) { if (hasAtLeastOneUninitializedMainDictionary()) { asyncReloadUninitializedMainDictionaries(context, newLocales, listener); } } if (listener != null) { listener.onUpdateMainDictionaryAvailability(hasInitializedMainDictionary()); listener.onUpdateMainDictionaryAvailability(hasAtLeastOneInitializedMainDictionary()); } // Clean up old dictionaries. for (final Locale localeToCleanUp : existingDictsToCleanup.keySet()) { final ArrayList<String> dictTypesToCleanUp = existingDictsToCleanup.get(localeToCleanUp); final DictionaryGroup dictionarySetToCleanup = oldDictionaryGroup; final DictionaryGroup dictionarySetToCleanup = findDictionaryGroupWithLocale(oldDictionaryGroups, localeToCleanUp); for (final String dictType : dictTypesToCleanUp) { dictionarySetToCleanup.closeDict(dictType); } Loading @@ -330,12 +346,18 @@ public class DictionaryFacilitator { private void asyncReloadUninitializedMainDictionaries(final Context context, final Locale[] locales, final DictionaryInitializationListener listener) { final CountDownLatch latchForWaitingLoadingMainDictionary = new CountDownLatch(1); mLatchForWaitingLoadingMainDictionary = latchForWaitingLoadingMainDictionary; mLatchForWaitingLoadingMainDictionaries = latchForWaitingLoadingMainDictionary; ExecutorUtils.getExecutor("InitializeBinaryDictionary").execute(new Runnable() { @Override public void run() { 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 = DictionaryFactory.createMainDictionaryFromManager(context, locale); synchronized (mLock) { Loading @@ -348,7 +370,8 @@ public class DictionaryFacilitator { } } if (listener != null) { listener.onUpdateMainDictionaryAvailability(hasInitializedMainDictionary()); listener.onUpdateMainDictionaryAvailability( hasAtLeastOneInitializedMainDictionary()); } latchForWaitingLoadingMainDictionary.countDown(); } Loading Loading @@ -381,18 +404,21 @@ public class DictionaryFacilitator { subDicts.put(dictType, dict); } } mDictionaryGroup = new DictionaryGroup(locale, mainDictionary, subDicts); mDictionaryGroups = new DictionaryGroup[] { new DictionaryGroup(locale, mainDictionary, subDicts) }; } public void closeDictionaries() { final DictionaryGroup dictionaryGroup; final DictionaryGroup[] dictionaryGroups; synchronized (mLock) { dictionaryGroup = mDictionaryGroup; mDictionaryGroup = new DictionaryGroup(); dictionaryGroups = mDictionaryGroups; mDictionaryGroups = new DictionaryGroup[] { new DictionaryGroup() }; } for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { dictionaryGroup.closeDict(dictType); } } mDistracterFilter.close(); if (mPersonalizationHelper != null) { mPersonalizationHelper.close(); Loading @@ -401,42 +427,73 @@ public class DictionaryFacilitator { @UsedForTesting 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 // of this method. public boolean hasInitializedMainDictionary() { final Dictionary mainDict = mDictionaryGroup.getDict(Dictionary.TYPE_MAIN); return mainDict != null && mainDict.isInitialized(); // The main dictionaries are loaded asynchronously. Don't cache the return value // of these methods. public boolean hasAtLeastOneInitializedMainDictionary() { 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 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() { 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() { final HashSet<ExpandableBinaryDictionary> personalizationDictsUsedForSuggestion = new HashSet<>(); final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { final ExpandableBinaryDictionary personalizationDictUsedForSuggestion = mDictionaryGroup.getSubDict(Dictionary.TYPE_PERSONALIZATION); dictionaryGroup.getSubDict(Dictionary.TYPE_PERSONALIZATION); personalizationDictsUsedForSuggestion.add(personalizationDictUsedForSuggestion); } mPersonalizationHelper.flushPersonalizationDictionariesToUpdate( personalizationDictUsedForSuggestion); personalizationDictsUsedForSuggestion); mDistracterFilter.close(); } public void waitForLoadingMainDictionary(final long timeout, final TimeUnit unit) public void waitForLoadingMainDictionaries(final long timeout, final TimeUnit unit) throws InterruptedException { mLatchForWaitingLoadingMainDictionary.await(timeout, unit); mLatchForWaitingLoadingMainDictionaries.await(timeout, unit); } @UsedForTesting public void waitForLoadingDictionariesForTesting(final long timeout, final TimeUnit unit) throws InterruptedException { waitForLoadingMainDictionary(timeout, unit); final Map<String, ExpandableBinaryDictionary> dictMap = mDictionaryGroup.mSubDictMap; for (final ExpandableBinaryDictionary dict : dictMap.values()) { waitForLoadingMainDictionaries(timeout, unit); final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { for (final ExpandableBinaryDictionary dict : dictionaryGroup.mSubDictMap.values()) { dict.waitAllTasksForTests(); } } } public boolean isUserDictionaryEnabled() { return mIsUserDictEnabled; Loading @@ -453,7 +510,7 @@ public class DictionaryFacilitator { public void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized, final PrevWordsInfo prevWordsInfo, final int timeStampInSeconds, final boolean blockPotentiallyOffensive) { final DictionaryGroup dictionaryGroup = mDictionaryGroup; final DictionaryGroup dictionaryGroup = getDictionaryGroupForActiveLanguage(); final String[] words = suggestion.split(Constants.WORD_SEPARATOR); PrevWordsInfo prevWordsInfoForCurrentWord = prevWordsInfo; for (int i = 0; i < words.length; i++) { Loading Loading @@ -520,7 +577,8 @@ public class DictionaryFacilitator { } private void removeWord(final String dictName, final String word) { final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictName); final ExpandableBinaryDictionary dictionary = getDictionaryGroupForActiveLanguage().getSubDict(dictName); if (dictionary != null) { dictionary.removeUnigramEntryDynamically(word); } Loading @@ -536,10 +594,11 @@ public class DictionaryFacilitator { public SuggestionResults getSuggestionResults(final WordComposer composer, final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) { final DictionaryGroup dictionaryGroup = mDictionaryGroup; final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; final SuggestionResults suggestionResults = new SuggestionResults(SuggestedWords.MAX_SUGGESTIONS); 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) { final Dictionary dictionary = dictionaryGroup.getDict(dictType); if (null == dictionary) continue; Loading @@ -552,6 +611,7 @@ public class DictionaryFacilitator { suggestionResults.mRawSuggestions.addAll(dictionarySuggestions); } } } return suggestionResults; } Loading @@ -559,9 +619,10 @@ public class DictionaryFacilitator { if (TextUtils.isEmpty(word)) { return false; } final DictionaryGroup dictionaryGroup = mDictionaryGroup; final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { if (dictionaryGroup.mLocale == null) { return false; continue; } final String lowerCasedWord = word.toLowerCase(dictionaryGroup.mLocale); for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { Loading @@ -575,6 +636,7 @@ public class DictionaryFacilitator { return true; } } } return false; } Loading @@ -584,7 +646,8 @@ public class DictionaryFacilitator { return 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) { final Dictionary dictionary = dictionaryGroup.getDict(dictType); if (dictionary == null) continue; Loading @@ -598,6 +661,7 @@ public class DictionaryFacilitator { maxFreq = tempFreq; } } } return maxFreq; } Loading @@ -610,11 +674,14 @@ public class DictionaryFacilitator { } 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) { dictionary.clear(); } } } public void clearUserHistoryDictionary() { clearSubDictionary(Dictionary.TYPE_USER_HISTORY); Loading @@ -641,8 +708,10 @@ public class DictionaryFacilitator { public void addPhraseToContextualDictionary(final String[] phrase, final int probability, 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 = mDictionaryGroup.getSubDict(Dictionary.TYPE_CONTEXTUAL); getDictionaryGroupForActiveLanguage().getSubDict(Dictionary.TYPE_CONTEXTUAL); if (contextualDict == null) { return; } Loading Loading @@ -675,7 +744,9 @@ public class DictionaryFacilitator { } 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) { Log.e(TAG, "Cannot dump " + dictName + ". " + "The dictionary is not being used for suggestion or cannot be dumped."); Loading @@ -683,15 +754,18 @@ public class DictionaryFacilitator { } dictToDump.dumpAllWordsForDebug(); } } public ArrayList<Pair<String, DictionaryStats>> getStatsOfEnabledSubDicts() { 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) { final ExpandableBinaryDictionary dictionary = dictionaryGroup.getSubDict(dictType); if (dictionary == null) continue; statsOfEnabledSubDicts.add(new Pair<>(dictType, dictionary.getDictionaryStats())); } } return statsOfEnabledSubDicts; } } java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java +1 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,7 @@ public class DictionaryFacilitatorLruCache { private void waitForLoadingMainDictionary(final DictionaryFacilitator dictionaryFacilitator) { for (int i = 0; i < MAX_RETRY_COUNT_FOR_WAITING_FOR_LOADING_DICT; i++) { try { dictionaryFacilitator.waitForLoadingMainDictionary( dictionaryFacilitator.waitForLoadingMainDictionaries( WAIT_FOR_LOADING_MAIN_DICT_IN_MILLISECONDS, TimeUnit.MILLISECONDS); return; } catch (final InterruptedException e) { Loading java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +10 −8 Original line number Diff line number Diff line Loading @@ -156,23 +156,25 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } 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) { asyncPreCheckAndExecuteTaskWithLock(lock, null /* preCheckTask */, task); private void asyncExecuteTaskWithLock(final Lock lock, final String executorName, final Runnable task) { asyncPreCheckAndExecuteTaskWithLock(lock, null /* preCheckTask */, executorName, task); } private void asyncPreCheckAndExecuteTaskWithWriteLock( 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. private void asyncPreCheckAndExecuteTaskWithLock(final Lock lock, final Callable<Boolean> preCheckTask, final Runnable task) { ExecutorUtils.getExecutor(mDictName).execute(new Runnable() { final Callable<Boolean> preCheckTask, final String executorName, final Runnable task) { ExecutorUtils.getExecutor(executorName).execute(new Runnable() { @Override public void run() { if (preCheckTask != null) { Loading Loading @@ -676,10 +678,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { public void dumpAllWordsForDebug() { reloadDictionaryIfRequired(); asyncExecuteTaskWithLock(mLock.readLock(), new Runnable() { asyncExecuteTaskWithLock(mLock.readLock(), "dumpAllWordsForDebug", new Runnable() { @Override public void run() { Log.d(TAG, "Dump dictionary: " + mDictName); Log.d(TAG, "Dump dictionary: " + mDictName + " for " + mLocale); try { final DictionaryHeader header = mBinaryDictionary.getHeader(); Log.d(TAG, "Format version: " + mBinaryDictionary.getFormatVersion()); Loading java/src/com/android/inputmethod/latin/LatinIME.java +1 −1 Original line number Diff line number Diff line Loading @@ -1000,7 +1000,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelUpdateSuggestionStrip(); mainKeyboardView.setMainDictionaryAvailability( mDictionaryFacilitator.hasInitializedMainDictionary()); mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary()); mainKeyboardView.setKeyPreviewPopupEnabled(currentSettingsValues.mKeyPreviewPopupOn, currentSettingsValues.mKeyPreviewPopupDismissDelay); mainKeyboardView.setSlidingKeyInputPreviewEnabled( Loading java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java +4 −4 Original line number Diff line number Diff line Loading @@ -88,17 +88,17 @@ public class PersonalizationHelperForDictionaryFacilitator { /** * 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. */ public void flushPersonalizationDictionariesToUpdate( final ExpandableBinaryDictionary personalizationDictUsedForSuggestion) { final HashSet<ExpandableBinaryDictionary> personalizationDictsUsedForSuggestion) { for (final ExpandableBinaryDictionary personalizationDict : mPersonalizationDictsToUpdate.values()) { personalizationDict.asyncFlushBinaryDictionary(); if (personalizationDict != personalizationDictUsedForSuggestion) { if (!personalizationDictsUsedForSuggestion.contains(personalizationDict)) { // Close if the dictionary is not being used for suggestion. personalizationDict.close(); } Loading Loading
java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +169 −95 Original line number Diff line number Diff line Loading @@ -61,9 +61,9 @@ public class DictionaryFacilitator { // dictionary. 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 volatile CountDownLatch mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0); private volatile CountDownLatch mLatchForWaitingLoadingMainDictionaries = new CountDownLatch(0); // To synchronize assigning mDictionaryGroup to ensure closing dictionaries. private final Object mLock = new Object(); private final DistracterFilter mDistracterFilter; Loading Loading @@ -193,8 +193,9 @@ public class DictionaryFacilitator { mPersonalizationHelper.setIsMonolingualUser(isMonolingualUser); } // TODO: remove this, replace with version returning multiple locales public Locale getLocale() { return mDictionaryGroup.mLocale; return mDictionaryGroups[0].mLocale; } private static ExpandableBinaryDictionary getSubDict(final String dictType, Loading Loading @@ -226,6 +227,21 @@ public class DictionaryFacilitator { 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, final Locale newLocaleToUse, final boolean useContactsDict, final boolean usePersonalizedDicts, Loading @@ -252,7 +268,7 @@ public class DictionaryFacilitator { final ArrayList<String> dictsForLocale = new ArrayList<>(); existingDictsToCleanup.put(newLocale, dictsForLocale); final DictionaryGroup currentDictionaryGroupForLocale = newLocale.equals(mDictionaryGroup.mLocale) ? mDictionaryGroup : null; findDictionaryGroupWithLocale(mDictionaryGroups, newLocale); if (null == currentDictionaryGroupForLocale) { continue; } Loading @@ -266,10 +282,11 @@ public class DictionaryFacilitator { } } final HashMap<Locale, DictionaryGroup> newDictionaryGroups = new HashMap<>(); for (final Locale newLocale : newLocales) { final DictionaryGroup[] newDictionaryGroups = new DictionaryGroup[newLocales.length]; for (int i = 0; i < newLocales.length; ++i) { final Locale newLocale = newLocales[i]; final DictionaryGroup dictionaryGroupForLocale = newLocale.equals(mDictionaryGroup.mLocale) ? mDictionaryGroup : null; findDictionaryGroupWithLocale(mDictionaryGroups, newLocale); final ArrayList<String> dictsToCleanupForLocale = existingDictsToCleanup.get(newLocale); final boolean noExistingDictsForThisLocale = (null == dictionaryGroupForLocale); Loading Loading @@ -297,30 +314,29 @@ public class DictionaryFacilitator { } subDicts.put(subDictType, subDict); } newDictionaryGroups.put(newLocale, new DictionaryGroup(newLocale, mainDict, subDicts)); newDictionaryGroups[i] = new DictionaryGroup(newLocale, mainDict, subDicts); } // Replace Dictionaries. // TODO: use multiple locales. final DictionaryGroup newDictionaryGroup = newDictionaryGroups.get(newLocaleToUse); final DictionaryGroup oldDictionaryGroup; final DictionaryGroup[] oldDictionaryGroups; synchronized (mLock) { oldDictionaryGroup = mDictionaryGroup; mDictionaryGroup = newDictionaryGroup; oldDictionaryGroups = mDictionaryGroups; mDictionaryGroups = newDictionaryGroups; mIsUserDictEnabled = UserBinaryDictionary.isEnabled(context); if (null == newDictionaryGroup.getDict(Dictionary.TYPE_MAIN)) { if (hasAtLeastOneUninitializedMainDictionary()) { asyncReloadUninitializedMainDictionaries(context, newLocales, listener); } } if (listener != null) { listener.onUpdateMainDictionaryAvailability(hasInitializedMainDictionary()); listener.onUpdateMainDictionaryAvailability(hasAtLeastOneInitializedMainDictionary()); } // Clean up old dictionaries. for (final Locale localeToCleanUp : existingDictsToCleanup.keySet()) { final ArrayList<String> dictTypesToCleanUp = existingDictsToCleanup.get(localeToCleanUp); final DictionaryGroup dictionarySetToCleanup = oldDictionaryGroup; final DictionaryGroup dictionarySetToCleanup = findDictionaryGroupWithLocale(oldDictionaryGroups, localeToCleanUp); for (final String dictType : dictTypesToCleanUp) { dictionarySetToCleanup.closeDict(dictType); } Loading @@ -330,12 +346,18 @@ public class DictionaryFacilitator { private void asyncReloadUninitializedMainDictionaries(final Context context, final Locale[] locales, final DictionaryInitializationListener listener) { final CountDownLatch latchForWaitingLoadingMainDictionary = new CountDownLatch(1); mLatchForWaitingLoadingMainDictionary = latchForWaitingLoadingMainDictionary; mLatchForWaitingLoadingMainDictionaries = latchForWaitingLoadingMainDictionary; ExecutorUtils.getExecutor("InitializeBinaryDictionary").execute(new Runnable() { @Override public void run() { 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 = DictionaryFactory.createMainDictionaryFromManager(context, locale); synchronized (mLock) { Loading @@ -348,7 +370,8 @@ public class DictionaryFacilitator { } } if (listener != null) { listener.onUpdateMainDictionaryAvailability(hasInitializedMainDictionary()); listener.onUpdateMainDictionaryAvailability( hasAtLeastOneInitializedMainDictionary()); } latchForWaitingLoadingMainDictionary.countDown(); } Loading Loading @@ -381,18 +404,21 @@ public class DictionaryFacilitator { subDicts.put(dictType, dict); } } mDictionaryGroup = new DictionaryGroup(locale, mainDictionary, subDicts); mDictionaryGroups = new DictionaryGroup[] { new DictionaryGroup(locale, mainDictionary, subDicts) }; } public void closeDictionaries() { final DictionaryGroup dictionaryGroup; final DictionaryGroup[] dictionaryGroups; synchronized (mLock) { dictionaryGroup = mDictionaryGroup; mDictionaryGroup = new DictionaryGroup(); dictionaryGroups = mDictionaryGroups; mDictionaryGroups = new DictionaryGroup[] { new DictionaryGroup() }; } for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { dictionaryGroup.closeDict(dictType); } } mDistracterFilter.close(); if (mPersonalizationHelper != null) { mPersonalizationHelper.close(); Loading @@ -401,42 +427,73 @@ public class DictionaryFacilitator { @UsedForTesting 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 // of this method. public boolean hasInitializedMainDictionary() { final Dictionary mainDict = mDictionaryGroup.getDict(Dictionary.TYPE_MAIN); return mainDict != null && mainDict.isInitialized(); // The main dictionaries are loaded asynchronously. Don't cache the return value // of these methods. public boolean hasAtLeastOneInitializedMainDictionary() { 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 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() { 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() { final HashSet<ExpandableBinaryDictionary> personalizationDictsUsedForSuggestion = new HashSet<>(); final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { final ExpandableBinaryDictionary personalizationDictUsedForSuggestion = mDictionaryGroup.getSubDict(Dictionary.TYPE_PERSONALIZATION); dictionaryGroup.getSubDict(Dictionary.TYPE_PERSONALIZATION); personalizationDictsUsedForSuggestion.add(personalizationDictUsedForSuggestion); } mPersonalizationHelper.flushPersonalizationDictionariesToUpdate( personalizationDictUsedForSuggestion); personalizationDictsUsedForSuggestion); mDistracterFilter.close(); } public void waitForLoadingMainDictionary(final long timeout, final TimeUnit unit) public void waitForLoadingMainDictionaries(final long timeout, final TimeUnit unit) throws InterruptedException { mLatchForWaitingLoadingMainDictionary.await(timeout, unit); mLatchForWaitingLoadingMainDictionaries.await(timeout, unit); } @UsedForTesting public void waitForLoadingDictionariesForTesting(final long timeout, final TimeUnit unit) throws InterruptedException { waitForLoadingMainDictionary(timeout, unit); final Map<String, ExpandableBinaryDictionary> dictMap = mDictionaryGroup.mSubDictMap; for (final ExpandableBinaryDictionary dict : dictMap.values()) { waitForLoadingMainDictionaries(timeout, unit); final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { for (final ExpandableBinaryDictionary dict : dictionaryGroup.mSubDictMap.values()) { dict.waitAllTasksForTests(); } } } public boolean isUserDictionaryEnabled() { return mIsUserDictEnabled; Loading @@ -453,7 +510,7 @@ public class DictionaryFacilitator { public void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized, final PrevWordsInfo prevWordsInfo, final int timeStampInSeconds, final boolean blockPotentiallyOffensive) { final DictionaryGroup dictionaryGroup = mDictionaryGroup; final DictionaryGroup dictionaryGroup = getDictionaryGroupForActiveLanguage(); final String[] words = suggestion.split(Constants.WORD_SEPARATOR); PrevWordsInfo prevWordsInfoForCurrentWord = prevWordsInfo; for (int i = 0; i < words.length; i++) { Loading Loading @@ -520,7 +577,8 @@ public class DictionaryFacilitator { } private void removeWord(final String dictName, final String word) { final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictName); final ExpandableBinaryDictionary dictionary = getDictionaryGroupForActiveLanguage().getSubDict(dictName); if (dictionary != null) { dictionary.removeUnigramEntryDynamically(word); } Loading @@ -536,10 +594,11 @@ public class DictionaryFacilitator { public SuggestionResults getSuggestionResults(final WordComposer composer, final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) { final DictionaryGroup dictionaryGroup = mDictionaryGroup; final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; final SuggestionResults suggestionResults = new SuggestionResults(SuggestedWords.MAX_SUGGESTIONS); 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) { final Dictionary dictionary = dictionaryGroup.getDict(dictType); if (null == dictionary) continue; Loading @@ -552,6 +611,7 @@ public class DictionaryFacilitator { suggestionResults.mRawSuggestions.addAll(dictionarySuggestions); } } } return suggestionResults; } Loading @@ -559,9 +619,10 @@ public class DictionaryFacilitator { if (TextUtils.isEmpty(word)) { return false; } final DictionaryGroup dictionaryGroup = mDictionaryGroup; final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { if (dictionaryGroup.mLocale == null) { return false; continue; } final String lowerCasedWord = word.toLowerCase(dictionaryGroup.mLocale); for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { Loading @@ -575,6 +636,7 @@ public class DictionaryFacilitator { return true; } } } return false; } Loading @@ -584,7 +646,8 @@ public class DictionaryFacilitator { return 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) { final Dictionary dictionary = dictionaryGroup.getDict(dictType); if (dictionary == null) continue; Loading @@ -598,6 +661,7 @@ public class DictionaryFacilitator { maxFreq = tempFreq; } } } return maxFreq; } Loading @@ -610,11 +674,14 @@ public class DictionaryFacilitator { } 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) { dictionary.clear(); } } } public void clearUserHistoryDictionary() { clearSubDictionary(Dictionary.TYPE_USER_HISTORY); Loading @@ -641,8 +708,10 @@ public class DictionaryFacilitator { public void addPhraseToContextualDictionary(final String[] phrase, final int probability, 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 = mDictionaryGroup.getSubDict(Dictionary.TYPE_CONTEXTUAL); getDictionaryGroupForActiveLanguage().getSubDict(Dictionary.TYPE_CONTEXTUAL); if (contextualDict == null) { return; } Loading Loading @@ -675,7 +744,9 @@ public class DictionaryFacilitator { } 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) { Log.e(TAG, "Cannot dump " + dictName + ". " + "The dictionary is not being used for suggestion or cannot be dumped."); Loading @@ -683,15 +754,18 @@ public class DictionaryFacilitator { } dictToDump.dumpAllWordsForDebug(); } } public ArrayList<Pair<String, DictionaryStats>> getStatsOfEnabledSubDicts() { 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) { final ExpandableBinaryDictionary dictionary = dictionaryGroup.getSubDict(dictType); if (dictionary == null) continue; statsOfEnabledSubDicts.add(new Pair<>(dictType, dictionary.getDictionaryStats())); } } return statsOfEnabledSubDicts; } }
java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java +1 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,7 @@ public class DictionaryFacilitatorLruCache { private void waitForLoadingMainDictionary(final DictionaryFacilitator dictionaryFacilitator) { for (int i = 0; i < MAX_RETRY_COUNT_FOR_WAITING_FOR_LOADING_DICT; i++) { try { dictionaryFacilitator.waitForLoadingMainDictionary( dictionaryFacilitator.waitForLoadingMainDictionaries( WAIT_FOR_LOADING_MAIN_DICT_IN_MILLISECONDS, TimeUnit.MILLISECONDS); return; } catch (final InterruptedException e) { Loading
java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +10 −8 Original line number Diff line number Diff line Loading @@ -156,23 +156,25 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } 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) { asyncPreCheckAndExecuteTaskWithLock(lock, null /* preCheckTask */, task); private void asyncExecuteTaskWithLock(final Lock lock, final String executorName, final Runnable task) { asyncPreCheckAndExecuteTaskWithLock(lock, null /* preCheckTask */, executorName, task); } private void asyncPreCheckAndExecuteTaskWithWriteLock( 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. private void asyncPreCheckAndExecuteTaskWithLock(final Lock lock, final Callable<Boolean> preCheckTask, final Runnable task) { ExecutorUtils.getExecutor(mDictName).execute(new Runnable() { final Callable<Boolean> preCheckTask, final String executorName, final Runnable task) { ExecutorUtils.getExecutor(executorName).execute(new Runnable() { @Override public void run() { if (preCheckTask != null) { Loading Loading @@ -676,10 +678,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { public void dumpAllWordsForDebug() { reloadDictionaryIfRequired(); asyncExecuteTaskWithLock(mLock.readLock(), new Runnable() { asyncExecuteTaskWithLock(mLock.readLock(), "dumpAllWordsForDebug", new Runnable() { @Override public void run() { Log.d(TAG, "Dump dictionary: " + mDictName); Log.d(TAG, "Dump dictionary: " + mDictName + " for " + mLocale); try { final DictionaryHeader header = mBinaryDictionary.getHeader(); Log.d(TAG, "Format version: " + mBinaryDictionary.getFormatVersion()); Loading
java/src/com/android/inputmethod/latin/LatinIME.java +1 −1 Original line number Diff line number Diff line Loading @@ -1000,7 +1000,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelUpdateSuggestionStrip(); mainKeyboardView.setMainDictionaryAvailability( mDictionaryFacilitator.hasInitializedMainDictionary()); mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary()); mainKeyboardView.setKeyPreviewPopupEnabled(currentSettingsValues.mKeyPreviewPopupOn, currentSettingsValues.mKeyPreviewPopupDismissDelay); mainKeyboardView.setSlidingKeyInputPreviewEnabled( Loading
java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java +4 −4 Original line number Diff line number Diff line Loading @@ -88,17 +88,17 @@ public class PersonalizationHelperForDictionaryFacilitator { /** * 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. */ public void flushPersonalizationDictionariesToUpdate( final ExpandableBinaryDictionary personalizationDictUsedForSuggestion) { final HashSet<ExpandableBinaryDictionary> personalizationDictsUsedForSuggestion) { for (final ExpandableBinaryDictionary personalizationDict : mPersonalizationDictsToUpdate.values()) { personalizationDict.asyncFlushBinaryDictionary(); if (personalizationDict != personalizationDictUsedForSuggestion) { if (!personalizationDictsUsedForSuggestion.contains(personalizationDict)) { // Close if the dictionary is not being used for suggestion. personalizationDict.close(); } Loading