diff options
author | 2024-12-16 21:45:41 -0500 | |
---|---|---|
committer | 2025-01-11 14:17:35 -0500 | |
commit | e9a0e66716dab4dd3184d009d8920de1961efdfa (patch) | |
tree | 02dcc096643d74645bf28459c2834c3d4a2ad7f2 /java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java | |
parent | fb3b9360d70596d7e921de8bf7d3ca99564a077e (diff) | |
download | latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.tar.gz latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.tar.xz latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.zip |
Rename to Kelar Keyboard (org.kelar.inputmethod.latin)
Diffstat (limited to 'java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java | 736 |
1 files changed, 0 insertions, 736 deletions
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java deleted file mode 100644 index b435de867..000000000 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java +++ /dev/null @@ -1,736 +0,0 @@ -/* -7 * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin; - -import android.Manifest; -import android.content.Context; -import android.text.TextUtils; -import android.util.Log; -import android.util.LruCache; - -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.latin.NgramContext.WordInfo; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.common.ComposedData; -import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.common.StringUtils; -import com.android.inputmethod.latin.permissions.PermissionsUtil; -import com.android.inputmethod.latin.personalization.UserHistoryDictionary; -import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; -import com.android.inputmethod.latin.utils.ExecutorUtils; -import com.android.inputmethod.latin.utils.SuggestionResults; - -import java.io.File; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -/** - * Facilitates interaction with different kinds of dictionaries. Provides APIs - * to instantiate and select the correct dictionaries (based on language or account), - * update entries and fetch suggestions. - * - * Currently AndroidSpellCheckerService and LatinIME both use DictionaryFacilitator as - * a client for interacting with dictionaries. - */ -public class DictionaryFacilitatorImpl implements DictionaryFacilitator { - // TODO: Consolidate dictionaries in native code. - public static final String TAG = DictionaryFacilitatorImpl.class.getSimpleName(); - - // HACK: This threshold is being used when adding a capitalized entry in the User History - // dictionary. - private static final int CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT = 140; - - private DictionaryGroup mDictionaryGroup = new DictionaryGroup(); - private volatile CountDownLatch mLatchForWaitingLoadingMainDictionaries = new CountDownLatch(0); - // To synchronize assigning mDictionaryGroup to ensure closing dictionaries. - private final Object mLock = new Object(); - - public static final Map<String, Class<? extends ExpandableBinaryDictionary>> - DICT_TYPE_TO_CLASS = new HashMap<>(); - - static { - DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_USER_HISTORY, UserHistoryDictionary.class); - DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_USER, UserBinaryDictionary.class); - DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_CONTACTS, ContactsBinaryDictionary.class); - } - - private static final String DICT_FACTORY_METHOD_NAME = "getDictionary"; - private static final Class<?>[] DICT_FACTORY_METHOD_ARG_TYPES = - new Class[] { Context.class, Locale.class, File.class, String.class, String.class }; - - private LruCache<String, Boolean> mValidSpellingWordReadCache; - private LruCache<String, Boolean> mValidSpellingWordWriteCache; - - @Override - public void setValidSpellingWordReadCache(final LruCache<String, Boolean> cache) { - mValidSpellingWordReadCache = cache; - } - - @Override - public void setValidSpellingWordWriteCache(final LruCache<String, Boolean> cache) { - mValidSpellingWordWriteCache = cache; - } - - @Override - public boolean isForLocale(final Locale locale) { - return locale != null && locale.equals(mDictionaryGroup.mLocale); - } - - /** - * Returns whether this facilitator is exactly for this account. - * - * @param account the account to test against. - */ - public boolean isForAccount(@Nullable final String account) { - return TextUtils.equals(mDictionaryGroup.mAccount, account); - } - - /** - * A group of dictionaries that work together for a single language. - */ - private static class DictionaryGroup { - // TODO: Add null analysis annotations. - // TODO: Run evaluation to determine a reasonable value for these constants. The current - // values are ad-hoc and chosen without any particular care or methodology. - public static final float WEIGHT_FOR_MOST_PROBABLE_LANGUAGE = 1.0f; - public static final float WEIGHT_FOR_GESTURING_IN_NOT_MOST_PROBABLE_LANGUAGE = 0.95f; - public static final float WEIGHT_FOR_TYPING_IN_NOT_MOST_PROBABLE_LANGUAGE = 0.6f; - - /** - * The locale associated with the dictionary group. - */ - @Nullable public final Locale mLocale; - - /** - * The user account associated with the dictionary group. - */ - @Nullable public final String mAccount; - - @Nullable private Dictionary mMainDict; - // Confidence that the most probable language is actually the language the user is - // typing in. For now, this is simply the number of times a word from this language - // has been committed in a row. - private int mConfidence = 0; - - public float mWeightForTypingInLocale = WEIGHT_FOR_MOST_PROBABLE_LANGUAGE; - public float mWeightForGesturingInLocale = WEIGHT_FOR_MOST_PROBABLE_LANGUAGE; - public final ConcurrentHashMap<String, ExpandableBinaryDictionary> mSubDictMap = - new ConcurrentHashMap<>(); - - public DictionaryGroup() { - this(null /* locale */, null /* mainDict */, null /* account */, - Collections.<String, ExpandableBinaryDictionary>emptyMap() /* subDicts */); - } - - public DictionaryGroup(@Nullable final Locale locale, - @Nullable final Dictionary mainDict, - @Nullable final String account, - final Map<String, ExpandableBinaryDictionary> subDicts) { - mLocale = locale; - mAccount = account; - // The main dictionary can be asynchronously loaded. - setMainDict(mainDict); - for (final Map.Entry<String, ExpandableBinaryDictionary> entry : subDicts.entrySet()) { - setSubDict(entry.getKey(), entry.getValue()); - } - } - - private void setSubDict(final String dictType, final ExpandableBinaryDictionary dict) { - if (dict != null) { - mSubDictMap.put(dictType, dict); - } - } - - public void setMainDict(final Dictionary mainDict) { - // Close old dictionary if exists. Main dictionary can be assigned multiple times. - final Dictionary oldDict = mMainDict; - mMainDict = mainDict; - if (oldDict != null && mainDict != oldDict) { - oldDict.close(); - } - } - - public Dictionary getDict(final String dictType) { - if (Dictionary.TYPE_MAIN.equals(dictType)) { - return mMainDict; - } - return getSubDict(dictType); - } - - public ExpandableBinaryDictionary getSubDict(final String dictType) { - return mSubDictMap.get(dictType); - } - - public boolean hasDict(final String dictType, @Nullable final String account) { - if (Dictionary.TYPE_MAIN.equals(dictType)) { - return mMainDict != null; - } - if (Dictionary.TYPE_USER_HISTORY.equals(dictType) && - !TextUtils.equals(account, mAccount)) { - // If the dictionary type is user history, & if the account doesn't match, - // return immediately. If the account matches, continue looking it up in the - // sub dictionary map. - return false; - } - return mSubDictMap.containsKey(dictType); - } - - public void closeDict(final String dictType) { - final Dictionary dict; - if (Dictionary.TYPE_MAIN.equals(dictType)) { - dict = mMainDict; - } else { - dict = mSubDictMap.remove(dictType); - } - if (dict != null) { - dict.close(); - } - } - } - - public DictionaryFacilitatorImpl() { - } - - @Override - public void onStartInput() { - } - - @Override - public void onFinishInput(Context context) { - } - - @Override - public boolean isActive() { - return mDictionaryGroup.mLocale != null; - } - - @Override - public Locale getLocale() { - return mDictionaryGroup.mLocale; - } - - @Override - public boolean usesContacts() { - return mDictionaryGroup.getSubDict(Dictionary.TYPE_CONTACTS) != null; - } - - @Override - public String getAccount() { - return null; - } - - @Nullable - private static ExpandableBinaryDictionary getSubDict(final String dictType, - final Context context, final Locale locale, final File dictFile, - final String dictNamePrefix, @Nullable final String account) { - final Class<? extends ExpandableBinaryDictionary> dictClass = - DICT_TYPE_TO_CLASS.get(dictType); - if (dictClass == null) { - return null; - } - try { - final Method factoryMethod = dictClass.getMethod(DICT_FACTORY_METHOD_NAME, - DICT_FACTORY_METHOD_ARG_TYPES); - final Object dict = factoryMethod.invoke(null /* obj */, - new Object[] { context, locale, dictFile, dictNamePrefix, account }); - return (ExpandableBinaryDictionary) dict; - } catch (final NoSuchMethodException | SecurityException | IllegalAccessException - | IllegalArgumentException | InvocationTargetException e) { - Log.e(TAG, "Cannot create dictionary: " + dictType, e); - return null; - } - } - - @Nullable - static DictionaryGroup findDictionaryGroupWithLocale(final DictionaryGroup dictionaryGroup, - final Locale locale) { - return locale.equals(dictionaryGroup.mLocale) ? dictionaryGroup : null; - } - - @Override - public void resetDictionaries( - final Context context, - final Locale newLocale, - final boolean useContactsDict, - final boolean usePersonalizedDicts, - final boolean forceReloadMainDictionary, - @Nullable final String account, - final String dictNamePrefix, - @Nullable final DictionaryInitializationListener listener) { - final HashMap<Locale, ArrayList<String>> existingDictionariesToCleanup = new HashMap<>(); - // TODO: Make subDictTypesToUse configurable by resource or a static final list. - final HashSet<String> subDictTypesToUse = new HashSet<>(); - subDictTypesToUse.add(Dictionary.TYPE_USER); - - // Do not use contacts dictionary if we do not have permissions to read contacts. - final boolean contactsPermissionGranted = PermissionsUtil.checkAllPermissionsGranted( - context, Manifest.permission.READ_CONTACTS); - if (useContactsDict && contactsPermissionGranted) { - subDictTypesToUse.add(Dictionary.TYPE_CONTACTS); - } - if (usePersonalizedDicts) { - subDictTypesToUse.add(Dictionary.TYPE_USER_HISTORY); - } - - // Gather all dictionaries. We'll remove them from the list to clean up later. - final ArrayList<String> dictTypeForLocale = new ArrayList<>(); - existingDictionariesToCleanup.put(newLocale, dictTypeForLocale); - final DictionaryGroup currentDictionaryGroupForLocale = - findDictionaryGroupWithLocale(mDictionaryGroup, newLocale); - if (currentDictionaryGroupForLocale != null) { - for (final String dictType : DYNAMIC_DICTIONARY_TYPES) { - if (currentDictionaryGroupForLocale.hasDict(dictType, account)) { - dictTypeForLocale.add(dictType); - } - } - if (currentDictionaryGroupForLocale.hasDict(Dictionary.TYPE_MAIN, account)) { - dictTypeForLocale.add(Dictionary.TYPE_MAIN); - } - } - - final DictionaryGroup dictionaryGroupForLocale = - findDictionaryGroupWithLocale(mDictionaryGroup, newLocale); - final ArrayList<String> dictTypesToCleanupForLocale = - existingDictionariesToCleanup.get(newLocale); - final boolean noExistingDictsForThisLocale = (null == dictionaryGroupForLocale); - - final Dictionary mainDict; - if (forceReloadMainDictionary || noExistingDictsForThisLocale - || !dictionaryGroupForLocale.hasDict(Dictionary.TYPE_MAIN, account)) { - mainDict = null; - } else { - mainDict = dictionaryGroupForLocale.getDict(Dictionary.TYPE_MAIN); - dictTypesToCleanupForLocale.remove(Dictionary.TYPE_MAIN); - } - - final Map<String, ExpandableBinaryDictionary> subDicts = new HashMap<>(); - for (final String subDictType : subDictTypesToUse) { - final ExpandableBinaryDictionary subDict; - if (noExistingDictsForThisLocale - || !dictionaryGroupForLocale.hasDict(subDictType, account)) { - // Create a new dictionary. - subDict = getSubDict(subDictType, context, newLocale, null /* dictFile */, - dictNamePrefix, account); - } else { - // Reuse the existing dictionary, and don't close it at the end - subDict = dictionaryGroupForLocale.getSubDict(subDictType); - dictTypesToCleanupForLocale.remove(subDictType); - } - subDicts.put(subDictType, subDict); - } - DictionaryGroup newDictionaryGroup = - new DictionaryGroup(newLocale, mainDict, account, subDicts); - - // Replace Dictionaries. - final DictionaryGroup oldDictionaryGroup; - synchronized (mLock) { - oldDictionaryGroup = mDictionaryGroup; - mDictionaryGroup = newDictionaryGroup; - if (hasAtLeastOneUninitializedMainDictionary()) { - asyncReloadUninitializedMainDictionaries(context, newLocale, listener); - } - } - if (listener != null) { - listener.onUpdateMainDictionaryAvailability(hasAtLeastOneInitializedMainDictionary()); - } - - // Clean up old dictionaries. - for (final Locale localeToCleanUp : existingDictionariesToCleanup.keySet()) { - final ArrayList<String> dictTypesToCleanUp = - existingDictionariesToCleanup.get(localeToCleanUp); - final DictionaryGroup dictionarySetToCleanup = - findDictionaryGroupWithLocale(oldDictionaryGroup, localeToCleanUp); - for (final String dictType : dictTypesToCleanUp) { - dictionarySetToCleanup.closeDict(dictType); - } - } - - if (mValidSpellingWordWriteCache != null) { - mValidSpellingWordWriteCache.evictAll(); - } - } - - private void asyncReloadUninitializedMainDictionaries(final Context context, - final Locale locale, final DictionaryInitializationListener listener) { - final CountDownLatch latchForWaitingLoadingMainDictionary = new CountDownLatch(1); - mLatchForWaitingLoadingMainDictionaries = latchForWaitingLoadingMainDictionary; - ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute(new Runnable() { - @Override - public void run() { - doReloadUninitializedMainDictionaries( - context, locale, listener, latchForWaitingLoadingMainDictionary); - } - }); - } - - void doReloadUninitializedMainDictionaries(final Context context, final Locale locale, - final DictionaryInitializationListener listener, - final CountDownLatch latchForWaitingLoadingMainDictionary) { - final DictionaryGroup dictionaryGroup = - findDictionaryGroupWithLocale(mDictionaryGroup, 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"); - return; - } - final Dictionary mainDict = - DictionaryFactory.createMainDictionaryFromManager(context, locale); - synchronized (mLock) { - if (locale.equals(dictionaryGroup.mLocale)) { - dictionaryGroup.setMainDict(mainDict); - } else { - // Dictionary facilitator has been reset for another locale. - mainDict.close(); - } - } - if (listener != null) { - listener.onUpdateMainDictionaryAvailability(hasAtLeastOneInitializedMainDictionary()); - } - latchForWaitingLoadingMainDictionary.countDown(); - } - - @UsedForTesting - public void resetDictionariesForTesting(final Context context, final Locale locale, - final ArrayList<String> dictionaryTypes, final HashMap<String, File> dictionaryFiles, - final Map<String, Map<String, String>> additionalDictAttributes, - @Nullable final String account) { - Dictionary mainDictionary = null; - final Map<String, ExpandableBinaryDictionary> subDicts = new HashMap<>(); - - for (final String dictType : dictionaryTypes) { - if (dictType.equals(Dictionary.TYPE_MAIN)) { - mainDictionary = DictionaryFactory.createMainDictionaryFromManager(context, - locale); - } else { - final File dictFile = dictionaryFiles.get(dictType); - final ExpandableBinaryDictionary dict = getSubDict( - dictType, context, locale, dictFile, "" /* dictNamePrefix */, account); - if (additionalDictAttributes.containsKey(dictType)) { - dict.clearAndFlushDictionaryWithAdditionalAttributes( - additionalDictAttributes.get(dictType)); - } - if (dict == null) { - throw new RuntimeException("Unknown dictionary type: " + dictType); - } - dict.reloadDictionaryIfRequired(); - dict.waitAllTasksForTests(); - subDicts.put(dictType, dict); - } - } - mDictionaryGroup = new DictionaryGroup(locale, mainDictionary, account, subDicts); - } - - public void closeDictionaries() { - final DictionaryGroup dictionaryGroupToClose; - synchronized (mLock) { - dictionaryGroupToClose = mDictionaryGroup; - mDictionaryGroup = new DictionaryGroup(); - } - for (final String dictType : ALL_DICTIONARY_TYPES) { - dictionaryGroupToClose.closeDict(dictType); - } - } - - @UsedForTesting - public ExpandableBinaryDictionary getSubDictForTesting(final String dictName) { - return mDictionaryGroup.getSubDict(dictName); - } - - // The main dictionaries are loaded asynchronously. Don't cache the return value - // of these methods. - public boolean hasAtLeastOneInitializedMainDictionary() { - final Dictionary mainDict = mDictionaryGroup.getDict(Dictionary.TYPE_MAIN); - if (mainDict != null && mainDict.isInitialized()) { - return true; - } - return false; - } - - public boolean hasAtLeastOneUninitializedMainDictionary() { - final Dictionary mainDict = mDictionaryGroup.getDict(Dictionary.TYPE_MAIN); - if (mainDict == null || !mainDict.isInitialized()) { - return true; - } - return false; - } - - public void waitForLoadingMainDictionaries(final long timeout, final TimeUnit unit) - throws InterruptedException { - mLatchForWaitingLoadingMainDictionaries.await(timeout, unit); - } - - @UsedForTesting - public void waitForLoadingDictionariesForTesting(final long timeout, final TimeUnit unit) - throws InterruptedException { - waitForLoadingMainDictionaries(timeout, unit); - for (final ExpandableBinaryDictionary dict : mDictionaryGroup.mSubDictMap.values()) { - dict.waitAllTasksForTests(); - } - } - - public void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized, - @Nonnull final NgramContext ngramContext, final long timeStampInSeconds, - final boolean blockPotentiallyOffensive) { - // Update the spelling cache before learning. Words that are not yet added to user history - // and appear in no other language model are not considered valid. - putWordIntoValidSpellingWordCache("addToUserHistory", suggestion); - - final String[] words = suggestion.split(Constants.WORD_SEPARATOR); - NgramContext ngramContextForCurrentWord = ngramContext; - for (int i = 0; i < words.length; i++) { - final String currentWord = words[i]; - final boolean wasCurrentWordAutoCapitalized = (i == 0) ? wasAutoCapitalized : false; - addWordToUserHistory(mDictionaryGroup, ngramContextForCurrentWord, currentWord, - wasCurrentWordAutoCapitalized, (int) timeStampInSeconds, - blockPotentiallyOffensive); - ngramContextForCurrentWord = - ngramContextForCurrentWord.getNextNgramContext(new WordInfo(currentWord)); - } - } - - private void putWordIntoValidSpellingWordCache( - @Nonnull final String caller, - @Nonnull final String originalWord) { - if (mValidSpellingWordWriteCache == null) { - return; - } - - final String lowerCaseWord = originalWord.toLowerCase(getLocale()); - final boolean lowerCaseValid = isValidSpellingWord(lowerCaseWord); - mValidSpellingWordWriteCache.put(lowerCaseWord, lowerCaseValid); - - final String capitalWord = - StringUtils.capitalizeFirstAndDowncaseRest(originalWord, getLocale()); - final boolean capitalValid; - if (lowerCaseValid) { - // The lower case form of the word is valid, so the upper case must be valid. - capitalValid = true; - } else { - capitalValid = isValidSpellingWord(capitalWord); - } - mValidSpellingWordWriteCache.put(capitalWord, capitalValid); - } - - private void addWordToUserHistory(final DictionaryGroup dictionaryGroup, - final NgramContext ngramContext, final String word, final boolean wasAutoCapitalized, - final int timeStampInSeconds, final boolean blockPotentiallyOffensive) { - final ExpandableBinaryDictionary userHistoryDictionary = - dictionaryGroup.getSubDict(Dictionary.TYPE_USER_HISTORY); - if (userHistoryDictionary == null || !isForLocale(userHistoryDictionary.mLocale)) { - return; - } - final int maxFreq = getFrequency(word); - if (maxFreq == 0 && blockPotentiallyOffensive) { - return; - } - final String lowerCasedWord = word.toLowerCase(dictionaryGroup.mLocale); - final String secondWord; - if (wasAutoCapitalized) { - if (isValidSuggestionWord(word) && !isValidSuggestionWord(lowerCasedWord)) { - // If the word was auto-capitalized and exists only as a capitalized word in the - // dictionary, then we must not downcase it before registering it. For example, - // the name of the contacts in start-of-sentence position would come here with the - // wasAutoCapitalized flag: if we downcase it, we'd register a lower-case version - // of that contact's name which would end up popping in suggestions. - secondWord = word; - } else { - // If however the word is not in the dictionary, or exists as a lower-case word - // only, then we consider that was a lower-case word that had been auto-capitalized. - secondWord = lowerCasedWord; - } - } else { - // HACK: We'd like to avoid adding the capitalized form of common words to the User - // History dictionary in order to avoid suggesting them until the dictionary - // consolidation is done. - // TODO: Remove this hack when ready. - final int lowerCaseFreqInMainDict = dictionaryGroup.hasDict(Dictionary.TYPE_MAIN, - null /* account */) ? - dictionaryGroup.getDict(Dictionary.TYPE_MAIN).getFrequency(lowerCasedWord) : - Dictionary.NOT_A_PROBABILITY; - if (maxFreq < lowerCaseFreqInMainDict - && lowerCaseFreqInMainDict >= CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT) { - // Use lower cased word as the word can be a distracter of the popular word. - secondWord = lowerCasedWord; - } else { - secondWord = word; - } - } - // We demote unrecognized words (frequency < 0, below) by specifying them as "invalid". - // We don't add words with 0-frequency (assuming they would be profanity etc.). - final boolean isValid = maxFreq > 0; - UserHistoryDictionary.addToDictionary(userHistoryDictionary, ngramContext, secondWord, - isValid, timeStampInSeconds); - } - - private void removeWord(final String dictName, final String word) { - final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictName); - if (dictionary != null) { - dictionary.removeUnigramEntryDynamically(word); - } - } - - @Override - public void unlearnFromUserHistory(final String word, - @Nonnull final NgramContext ngramContext, final long timeStampInSeconds, - final int eventType) { - // TODO: Decide whether or not to remove the word on EVENT_BACKSPACE. - if (eventType != Constants.EVENT_BACKSPACE) { - removeWord(Dictionary.TYPE_USER_HISTORY, word); - } - - // Update the spelling cache after unlearning. Words that are removed from user history - // and appear in no other language model are not considered valid. - putWordIntoValidSpellingWordCache("unlearnFromUserHistory", word.toLowerCase()); - } - - // TODO: Revise the way to fusion suggestion results. - @Override - @Nonnull public SuggestionResults getSuggestionResults(ComposedData composedData, - NgramContext ngramContext, @Nonnull final Keyboard keyboard, - SettingsValuesForSuggestion settingsValuesForSuggestion, int sessionId, - int inputStyle) { - long proximityInfoHandle = keyboard.getProximityInfo().getNativeProximityInfo(); - final SuggestionResults suggestionResults = new SuggestionResults( - SuggestedWords.MAX_SUGGESTIONS, ngramContext.isBeginningOfSentenceContext(), - false /* firstSuggestionExceedsConfidenceThreshold */); - final float[] weightOfLangModelVsSpatialModel = - new float[] { Dictionary.NOT_A_WEIGHT_OF_LANG_MODEL_VS_SPATIAL_MODEL }; - for (final String dictType : ALL_DICTIONARY_TYPES) { - final Dictionary dictionary = mDictionaryGroup.getDict(dictType); - if (null == dictionary) continue; - final float weightForLocale = composedData.mIsBatchMode - ? mDictionaryGroup.mWeightForGesturingInLocale - : mDictionaryGroup.mWeightForTypingInLocale; - final ArrayList<SuggestedWordInfo> dictionarySuggestions = - dictionary.getSuggestions(composedData, ngramContext, - proximityInfoHandle, settingsValuesForSuggestion, sessionId, - weightForLocale, weightOfLangModelVsSpatialModel); - if (null == dictionarySuggestions) continue; - suggestionResults.addAll(dictionarySuggestions); - if (null != suggestionResults.mRawSuggestions) { - suggestionResults.mRawSuggestions.addAll(dictionarySuggestions); - } - } - return suggestionResults; - } - - public boolean isValidSpellingWord(final String word) { - if (mValidSpellingWordReadCache != null) { - final Boolean cachedValue = mValidSpellingWordReadCache.get(word); - if (cachedValue != null) { - return cachedValue; - } - } - - return isValidWord(word, ALL_DICTIONARY_TYPES); - } - - public boolean isValidSuggestionWord(final String word) { - return isValidWord(word, ALL_DICTIONARY_TYPES); - } - - private boolean isValidWord(final String word, final String[] dictionariesToCheck) { - if (TextUtils.isEmpty(word)) { - return false; - } - if (mDictionaryGroup.mLocale == null) { - return false; - } - for (final String dictType : dictionariesToCheck) { - final Dictionary dictionary = mDictionaryGroup.getDict(dictType); - // Ideally the passed map would come out of a {@link java.util.concurrent.Future} and - // would be immutable once it's finished initializing, but concretely a null test is - // probably good enough for the time being. - if (null == dictionary) continue; - if (dictionary.isValidWord(word)) { - return true; - } - } - return false; - } - - private int getFrequency(final String word) { - if (TextUtils.isEmpty(word)) { - return Dictionary.NOT_A_PROBABILITY; - } - int maxFreq = Dictionary.NOT_A_PROBABILITY; - for (final String dictType : ALL_DICTIONARY_TYPES) { - final Dictionary dictionary = mDictionaryGroup.getDict(dictType); - if (dictionary == null) continue; - final int tempFreq = dictionary.getFrequency(word); - if (tempFreq >= maxFreq) { - maxFreq = tempFreq; - } - } - return maxFreq; - } - - private boolean clearSubDictionary(final String dictName) { - final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictName); - if (dictionary == null) { - return false; - } - dictionary.clear(); - return true; - } - - @Override - public boolean clearUserHistoryDictionary(final Context context) { - return clearSubDictionary(Dictionary.TYPE_USER_HISTORY); - } - - @Override - public void dumpDictionaryForDebug(final String dictName) { - final ExpandableBinaryDictionary dictToDump = mDictionaryGroup.getSubDict(dictName); - if (dictToDump == null) { - Log.e(TAG, "Cannot dump " + dictName + ". " - + "The dictionary is not being used for suggestion or cannot be dumped."); - return; - } - dictToDump.dumpAllWordsForDebug(); - } - - @Override - @Nonnull public List<DictionaryStats> getDictionaryStats(final Context context) { - final ArrayList<DictionaryStats> statsOfEnabledSubDicts = new ArrayList<>(); - for (final String dictType : DYNAMIC_DICTIONARY_TYPES) { - final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictType); - if (dictionary == null) continue; - statsOfEnabledSubDicts.add(dictionary.getDictionaryStats()); - } - return statsOfEnabledSubDicts; - } - - @Override - public String dump(final Context context) { - return ""; - } -} |