Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8cd53266 authored by Jean Chalard's avatar Jean Chalard
Browse files

[ML7] Have multiple DictionaryGroup instances in facilitator

This is the central change of multilingual input.

Bug: 11230254
Change-Id: Id8b68fb101e837e8cf182ab4bc1e55e4da5cc49d
parent 97b46504
Loading
Loading
Loading
Loading
+169 −95
Original line number Diff line number Diff line
@@ -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;
@@ -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,
@@ -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,
@@ -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;
            }
@@ -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);

@@ -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);
            }
@@ -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) {
@@ -348,7 +370,8 @@ public class DictionaryFacilitator {
                    }
                }
                if (listener != null) {
                    listener.onUpdateMainDictionaryAvailability(hasInitializedMainDictionary());
                    listener.onUpdateMainDictionaryAvailability(
                            hasAtLeastOneInitializedMainDictionary());
                }
                latchForWaitingLoadingMainDictionary.countDown();
            }
@@ -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();
@@ -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;
@@ -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++) {
@@ -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);
        }
@@ -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;
@@ -552,6 +611,7 @@ public class DictionaryFacilitator {
                    suggestionResults.mRawSuggestions.addAll(dictionarySuggestions);
                }
            }
        }
        return suggestionResults;
    }

@@ -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) {
@@ -575,6 +636,7 @@ public class DictionaryFacilitator {
                    return true;
                }
            }
        }
        return false;
    }

@@ -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;
@@ -598,6 +661,7 @@ public class DictionaryFacilitator {
                    maxFreq = tempFreq;
                }
            }
        }
        return maxFreq;
    }

@@ -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);
@@ -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;
        }
@@ -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.");
@@ -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;
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -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) {
+10 −8
Original line number Diff line number Diff line
@@ -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) {
@@ -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());
+1 −1
Original line number Diff line number Diff line
@@ -998,7 +998,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
        mHandler.cancelUpdateSuggestionStrip();

        mainKeyboardView.setMainDictionaryAvailability(
                mDictionaryFacilitator.hasInitializedMainDictionary());
                mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary());
        mainKeyboardView.setKeyPreviewPopupEnabled(currentSettingsValues.mKeyPreviewPopupOn,
                currentSettingsValues.mKeyPreviewPopupDismissDelay);
        mainKeyboardView.setSlidingKeyInputPreviewEnabled(
+4 −4
Original line number Diff line number Diff line
@@ -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