diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 59763c0fc..22fd90795 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -39,6 +39,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +import javax.annotation.Nullable; + public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { private static final String[] PROJECTION = {BaseColumns._ID, Contacts.DISPLAY_NAME}; @@ -86,7 +88,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { // Note: This method is called by {@link DictionaryFacilitator} using Java reflection. @ExternallyReferenced public static ContactsBinaryDictionary getDictionary(final Context context, final Locale locale, - final File dictFile, final String dictNamePrefix) { + final File dictFile, final String dictNamePrefix, @Nullable final String account) { return new ContactsBinaryDictionary(context, locale, dictFile, dictNamePrefix + NAME); } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index acf9cf10c..b8893a5d8 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -44,6 +44,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -56,8 +57,16 @@ import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import javax.annotation.Nullable; -// TODO: Consolidate dictionaries in native code. +/** + * 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 DictionaryFacilitator { + // TODO: Consolidate dictionaries in native code. public static final String TAG = DictionaryFacilitator.class.getSimpleName(); // HACK: This threshold is being used when adding a capitalized entry in the User History @@ -99,7 +108,7 @@ public class DictionaryFacilitator { 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 }; + new Class[] { Context.class, Locale.class, File.class, String.class, String.class }; private static final String[] SUB_DICT_TYPES = Arrays.copyOfRange(DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS, 1 /* start */, @@ -107,8 +116,8 @@ public class DictionaryFacilitator { /** * Returns whether this facilitator is exactly for this list of locales. + * * @param locales the list of locales to test against - * @return true if this facilitator handles exactly this list of locales, false otherwise */ public boolean isForLocales(final Locale[] locales) { if (locales.length != mDictionaryGroups.length) { @@ -129,34 +138,63 @@ public class DictionaryFacilitator { return true; } + /** + * Returns whether this facilitator is exactly for this account. + * + * @param account the account to test against. + */ + public boolean isForAccount(@Nullable final String account) { + for (final DictionaryGroup group : mDictionaryGroups) { + if (!TextUtils.equals(group.mAccount, account)) { + return false; + } + } + return true; + } + /** * 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; - public final Locale mLocale; - private Dictionary mMainDict; + /** + * 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 mSubDictMap = new ConcurrentHashMap<>(); public DictionaryGroup() { - mLocale = null; + this(null /* locale */, null /* mainDict */, null /* account */, + Collections.emptyMap() /* subDicts */); } - public DictionaryGroup(final Locale locale, final Dictionary mainDict, + public DictionaryGroup(@Nullable final Locale locale, + @Nullable final Dictionary mainDict, + @Nullable final String account, final Map subDicts) { mLocale = locale; + mAccount = account; // The main dictionary can be asynchronously loaded. setMainDict(mainDict); for (final Map.Entry entry : subDicts.entrySet()) { @@ -190,10 +228,17 @@ public class DictionaryFacilitator { return mSubDictMap.get(dictType); } - public boolean hasDict(final String 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); } @@ -310,7 +355,7 @@ public class DictionaryFacilitator { @Nullable private static ExpandableBinaryDictionary getSubDict(final String dictType, final Context context, final Locale locale, final File dictFile, - final String dictNamePrefix) { + final String dictNamePrefix, @Nullable final String account) { final Class dictClass = DICT_TYPE_TO_CLASS.get(dictType); if (dictClass == null) { @@ -320,7 +365,7 @@ public class DictionaryFacilitator { 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 }); + new Object[] { context, locale, dictFile, dictNamePrefix, account }); return (ExpandableBinaryDictionary) dict; } catch (final NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { @@ -332,17 +377,19 @@ public class DictionaryFacilitator { public void resetDictionaries(final Context context, final Locale[] newLocales, final boolean useContactsDict, final boolean usePersonalizedDicts, final boolean forceReloadMainDictionary, + @Nullable final String account, final DictionaryInitializationListener listener) { resetDictionariesWithDictNamePrefix(context, newLocales, useContactsDict, - usePersonalizedDicts, forceReloadMainDictionary, listener, "" /* dictNamePrefix */); + usePersonalizedDicts, forceReloadMainDictionary, listener, "" /* dictNamePrefix */, + account); } @Nullable static 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]; + for (DictionaryGroup dictionaryGroup : dictionaryGroups) { + if (locale.equals(dictionaryGroup.mLocale)) { + return dictionaryGroup; } } return null; @@ -350,11 +397,13 @@ public class DictionaryFacilitator { public void resetDictionariesWithDictNamePrefix(final Context context, final Locale[] newLocales, - final boolean useContactsDict, final boolean usePersonalizedDicts, + final boolean useContactsDict, + final boolean usePersonalizedDicts, final boolean forceReloadMainDictionary, @Nullable final DictionaryInitializationListener listener, - final String dictNamePrefix) { - final HashMap> existingDictsToCleanup = new HashMap<>(); + final String dictNamePrefix, + @Nullable final String account) { + final HashMap> existingDictionariesToCleanup = new HashMap<>(); // TODO: Make subDictTypesToUse configurable by resource or a static final list. final HashSet subDictTypesToUse = new HashSet<>(); subDictTypesToUse.add(Dictionary.TYPE_USER); @@ -369,20 +418,20 @@ public class DictionaryFacilitator { // Gather all dictionaries. We'll remove them from the list to clean up later. for (final Locale newLocale : newLocales) { - final ArrayList dictsForLocale = new ArrayList<>(); - existingDictsToCleanup.put(newLocale, dictsForLocale); + final ArrayList dictTypeForLocale = new ArrayList<>(); + existingDictionariesToCleanup.put(newLocale, dictTypeForLocale); final DictionaryGroup currentDictionaryGroupForLocale = findDictionaryGroupWithLocale(mDictionaryGroups, newLocale); if (null == currentDictionaryGroupForLocale) { continue; } for (final String dictType : SUB_DICT_TYPES) { - if (currentDictionaryGroupForLocale.hasDict(dictType)) { - dictsForLocale.add(dictType); + if (currentDictionaryGroupForLocale.hasDict(dictType, account)) { + dictTypeForLocale.add(dictType); } } - if (currentDictionaryGroupForLocale.hasDict(Dictionary.TYPE_MAIN)) { - dictsForLocale.add(Dictionary.TYPE_MAIN); + if (currentDictionaryGroupForLocale.hasDict(Dictionary.TYPE_MAIN, account)) { + dictTypeForLocale.add(Dictionary.TYPE_MAIN); } } @@ -391,34 +440,35 @@ public class DictionaryFacilitator { final Locale newLocale = newLocales[i]; final DictionaryGroup dictionaryGroupForLocale = findDictionaryGroupWithLocale(mDictionaryGroups, newLocale); - final ArrayList dictsToCleanupForLocale = existingDictsToCleanup.get(newLocale); + final ArrayList dictTypesToCleanupForLocale = + existingDictionariesToCleanup.get(newLocale); final boolean noExistingDictsForThisLocale = (null == dictionaryGroupForLocale); final Dictionary mainDict; if (forceReloadMainDictionary || noExistingDictsForThisLocale - || !dictionaryGroupForLocale.hasDict(Dictionary.TYPE_MAIN)) { + || !dictionaryGroupForLocale.hasDict(Dictionary.TYPE_MAIN, account)) { mainDict = null; } else { mainDict = dictionaryGroupForLocale.getDict(Dictionary.TYPE_MAIN); - dictsToCleanupForLocale.remove(Dictionary.TYPE_MAIN); + dictTypesToCleanupForLocale.remove(Dictionary.TYPE_MAIN); } final Map subDicts = new HashMap<>(); for (final String subDictType : subDictTypesToUse) { final ExpandableBinaryDictionary subDict; if (noExistingDictsForThisLocale - || !dictionaryGroupForLocale.hasDict(subDictType)) { + || !dictionaryGroupForLocale.hasDict(subDictType, account)) { // Create a new dictionary. subDict = getSubDict(subDictType, context, newLocale, null /* dictFile */, - dictNamePrefix); + dictNamePrefix, account); } else { // Reuse the existing dictionary, and don't close it at the end subDict = dictionaryGroupForLocale.getSubDict(subDictType); - dictsToCleanupForLocale.remove(subDictType); + dictTypesToCleanupForLocale.remove(subDictType); } subDicts.put(subDictType, subDict); } - newDictionaryGroups[i] = new DictionaryGroup(newLocale, mainDict, subDicts); + newDictionaryGroups[i] = new DictionaryGroup(newLocale, mainDict, account, subDicts); } // Replace Dictionaries. @@ -437,9 +487,9 @@ public class DictionaryFacilitator { } // Clean up old dictionaries. - for (final Locale localeToCleanUp : existingDictsToCleanup.keySet()) { + for (final Locale localeToCleanUp : existingDictionariesToCleanup.keySet()) { final ArrayList dictTypesToCleanUp = - existingDictsToCleanup.get(localeToCleanUp); + existingDictionariesToCleanup.get(localeToCleanUp); final DictionaryGroup dictionarySetToCleanup = findDictionaryGroupWithLocale(oldDictionaryGroups, localeToCleanUp); for (final String dictType : dictTypesToCleanUp) { @@ -493,7 +543,8 @@ public class DictionaryFacilitator { @UsedForTesting public void resetDictionariesForTesting(final Context context, final Locale[] locales, final ArrayList dictionaryTypes, final HashMap dictionaryFiles, - final Map> additionalDictAttributes) { + final Map> additionalDictAttributes, + @Nullable final String account) { Dictionary mainDictionary = null; final Map subDicts = new HashMap<>(); @@ -507,7 +558,7 @@ public class DictionaryFacilitator { } else { final File dictFile = dictionaryFiles.get(dictType); final ExpandableBinaryDictionary dict = getSubDict( - dictType, context, locale, dictFile, "" /* dictNamePrefix */); + dictType, context, locale, dictFile, "" /* dictNamePrefix */, account); if (additionalDictAttributes.containsKey(dictType)) { dict.clearAndFlushDictionaryWithAdditionalAttributes( additionalDictAttributes.get(dictType)); @@ -520,7 +571,7 @@ public class DictionaryFacilitator { subDicts.put(dictType, dict); } } - dictionaryGroups[i] = new DictionaryGroup(locale, mainDictionary, subDicts); + dictionaryGroups[i] = new DictionaryGroup(locale, mainDictionary, account, subDicts); } mDictionaryGroups = dictionaryGroups; mMostProbableDictionaryGroup = dictionaryGroups[0]; @@ -576,7 +627,7 @@ public class DictionaryFacilitator { public boolean hasPersonalizationDictionary() { final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { - if (dictionaryGroup.hasDict(Dictionary.TYPE_PERSONALIZATION)) { + if (dictionaryGroup.hasDict(Dictionary.TYPE_PERSONALIZATION, null /* account */)) { return true; } } @@ -677,7 +728,8 @@ public class DictionaryFacilitator { // 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) ? + final int lowerCaseFreqInMainDict = dictionaryGroup.hasDict(Dictionary.TYPE_MAIN, + null /* account */) ? dictionaryGroup.getDict(Dictionary.TYPE_MAIN).getFrequency(lowerCasedWord) : Dictionary.NOT_A_PROBABILITY; if (maxFreq < lowerCaseFreqInMainDict diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java index b578159eb..3119ff82f 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java @@ -102,10 +102,12 @@ public class DictionaryFacilitatorLruCache { private void resetDictionariesForLocaleLocked(final DictionaryFacilitator dictionaryFacilitator, final Locale locale) { + // Note: Given that personalized dictionaries are not used here; we can pass null account. dictionaryFacilitator.resetDictionariesWithDictNamePrefix(mContext, new Locale[] { locale }, mUseContactsDictionary, false /* usePersonalizedDicts */, false /* forceReloadMainDictionary */, null /* listener */, - mDictionaryNamePrefix); + mDictionaryNamePrefix, + null /* account */); } public void setUseContactsDictionary(final boolean useContectsDictionary) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index d6ec57fe6..1525a9264 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -633,12 +633,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // been displayed. Opening dictionaries never affects responsivity as dictionaries are // asynchronously loaded. if (!mHandler.hasPendingReopenDictionaries()) { - resetDictionaryFacilitatorForLocale(locales); + resetDictionaryFacilitator(locales); } mDictionaryFacilitator.updateEnabledSubtypes(mRichImm.getMyEnabledInputMethodSubtypeList( true /* allowsImplicitlySelectedSubtypes */)); refreshPersonalizationDictionarySession(currentSettingsValues); mStatsUtilsManager.onLoadSettings(currentSettingsValues); + resetDictionaryFacilitatorIfNecessary(); } private void refreshPersonalizationDictionarySession( @@ -676,7 +677,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen void resetDictionaryFacilitatorIfNecessary() { final Locale[] subtypeSwitcherLocales = mRichImm.getCurrentSubtypeLocales(); - if (mDictionaryFacilitator.isForLocales(subtypeSwitcherLocales)) { + if (mDictionaryFacilitator.isForLocales(subtypeSwitcherLocales) + && mDictionaryFacilitator.isForAccount(mSettings.getCurrent().mAccount)) { return; } final Locale[] subtypeLocales; @@ -690,20 +692,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { subtypeLocales = subtypeSwitcherLocales; } - resetDictionaryFacilitatorForLocale(subtypeLocales); + resetDictionaryFacilitator(subtypeLocales); } /** - * Reset the facilitator by loading dictionaries for the locales and the current settings values + * Reset the facilitator by loading dictionaries for the locales and + * the current settings values. * * @param locales the locales */ - // TODO: make sure the current settings always have the right locales, and read from them - private void resetDictionaryFacilitatorForLocale(final Locale[] locales) { + // TODO: make sure the current settings always have the right locales, and read from them. + private void resetDictionaryFacilitator(final Locale[] locales) { final SettingsValues settingsValues = mSettings.getCurrent(); mDictionaryFacilitator.resetDictionaries(this /* context */, locales, settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts, - false /* forceReloadMainDictionary */, this); + false /* forceReloadMainDictionary */, + settingsValues.mAccount, + this /* DictionaryInitializationListener */); if (settingsValues.mAutoCorrectionEnabledPerUserSettings) { mInputLogic.mSuggest.setAutoCorrectionThreshold( settingsValues.mAutoCorrectionThreshold); @@ -718,7 +723,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SettingsValues settingsValues = mSettings.getCurrent(); mDictionaryFacilitator.resetDictionaries(this /* context */, mDictionaryFacilitator.getLocales(), settingsValues.mUseContactsDict, - settingsValues.mUsePersonalizedDicts, true /* forceReloadMainDictionary */, this); + settingsValues.mUsePersonalizedDicts, + true /* forceReloadMainDictionary */, + settingsValues.mAccount, + this /* DictionaryInitializationListener */); } @Override @@ -1934,7 +1942,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SettingsValues settingsValues = mSettings.getCurrent(); mDictionaryFacilitator.resetDictionaries(this, new Locale[] { locale }, settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts, - false /* forceReloadMainDictionary */, this /* listener */); + false /* forceReloadMainDictionary */, + settingsValues.mAccount, + this /* DictionaryInitializationListener */); } // DO NOT USE THIS for any other purpose than testing. diff --git a/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java index 2dbab0a3f..8926c06b1 100644 --- a/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java @@ -114,7 +114,7 @@ public class PersonalizationHelperForDictionaryFacilitator { return personalizationDict; } personalizationDict = PersonalizationDictionary.getDictionary(context, locale, - null /* dictFile */, "" /* dictNamePrefix */); + null /* dictFile */, "" /* dictNamePrefix */, null /* account */); mPersonalizationDictsToUpdate.put(locale, personalizationDict); return personalizationDict; } diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java index 2b7fb1748..2d2b3d0a6 100644 --- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java @@ -36,6 +36,8 @@ import java.io.File; import java.util.Arrays; import java.util.Locale; +import javax.annotation.Nullable; + /** * An expandable dictionary that stores the words in the user dictionary provider into a binary * dictionary file to use it from native code. @@ -104,7 +106,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { // Note: This method is called by {@link DictionaryFacilitator} using Java reflection. @ExternallyReferenced public static UserBinaryDictionary getDictionary(final Context context, final Locale locale, - final File dictFile, final String dictNamePrefix) { + final File dictFile, final String dictNamePrefix, @Nullable final String account) { return new UserBinaryDictionary(context, locale, false /* alsoUseMoreRestrictiveLocales */, dictFile, dictNamePrefix + NAME); } diff --git a/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java b/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java index 39d9596ef..f663fe96a 100644 --- a/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java @@ -25,6 +25,8 @@ import com.android.inputmethod.latin.ExpandableBinaryDictionary; import java.io.File; import java.util.Locale; +import javax.annotation.Nullable; + public class ContextualDictionary extends ExpandableBinaryDictionary { /* package */ static final String NAME = ContextualDictionary.class.getSimpleName(); @@ -40,7 +42,7 @@ public class ContextualDictionary extends ExpandableBinaryDictionary { @SuppressWarnings("unused") @ExternallyReferenced public static ContextualDictionary getDictionary(final Context context, final Locale locale, - final File dictFile, final String dictNamePrefix) { + final File dictFile, final String dictNamePrefix, @Nullable final String account) { return new ContextualDictionary(context, locale, dictFile); } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java index 33d1273f7..76451cc6b 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java @@ -24,6 +24,8 @@ import com.android.inputmethod.latin.Dictionary; import java.io.File; import java.util.Locale; +import javax.annotation.Nullable; + public class PersonalizationDictionary extends DecayingExpandableBinaryDictionaryBase { /* package */ static final String NAME = PersonalizationDictionary.class.getSimpleName(); @@ -37,7 +39,8 @@ public class PersonalizationDictionary extends DecayingExpandableBinaryDictionar @SuppressWarnings("unused") @ExternallyReferenced public static PersonalizationDictionary getDictionary(final Context context, - final Locale locale, final File dictFile, final String dictNamePrefix) { + final Locale locale, final File dictFile, final String dictNamePrefix, + @Nullable final String account) { return PersonalizationHelper.getPersonalizationDictionary(context, locale); } } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java index b595f3974..4231450c1 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java @@ -64,7 +64,8 @@ public class PersonalizationHelper { return dict; } } - final UserHistoryDictionary dict = new UserHistoryDictionary(context, locale); + final UserHistoryDictionary dict = new UserHistoryDictionary( + context, locale, accountName); sLangUserHistoryDictCache.put(lookupStr, new SoftReference<>(dict)); return dict; } diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java index 946835cbc..2e41027a4 100644 --- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java @@ -37,20 +37,18 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; /** - * Locally gathers stats about the words user types and various other signals like auto-correction - * cancellation or manual picks. This allows the keyboard to adapt to the typist over time. + * Locally gathers statistics about the words user types and various other signals like + * auto-correction cancellation or manual picks. This allows the keyboard to adapt to the + * typist over time. */ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBase { static final String NAME = UserHistoryDictionary.class.getSimpleName(); // TODO: Make this constructor private - UserHistoryDictionary(final Context context, final Locale locale) { + UserHistoryDictionary(final Context context, final Locale locale, + @Nullable final String account) { super(context, - getUserHistoryDictName( - NAME, - locale, - null /* dictFile */, - context), + getUserHistoryDictName(NAME, locale, null /* dictFile */, account), locale, Dictionary.TYPE_USER_HISTORY, null /* dictFile */); @@ -61,24 +59,21 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas */ @UsedForTesting static String getUserHistoryDictName(final String name, final Locale locale, - @Nullable final File dictFile, final Context context) { + @Nullable final File dictFile, @Nullable final String account) { if (!ProductionFlags.ENABLE_PER_ACCOUNT_USER_HISTORY_DICTIONARY) { return getDictName(name, locale, dictFile); } - return getUserHistoryDictNamePerAccount(name, locale, dictFile, context); + return getUserHistoryDictNamePerAccount(name, locale, dictFile, account); } /** * Uses the currently signed in account to determine the dictionary name. */ private static String getUserHistoryDictNamePerAccount(final String name, final Locale locale, - @Nullable final File dictFile, final Context context) { + @Nullable final File dictFile, @Nullable final String account) { if (dictFile != null) { return dictFile.getName(); } - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - final String account = prefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, - null /* default */); String dictName = name + "." + locale.toString(); if (account != null) { dictName += "." + account; @@ -90,14 +85,7 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas @SuppressWarnings("unused") @ExternallyReferenced public static UserHistoryDictionary getDictionary(final Context context, final Locale locale, - final File dictFile, final String dictNamePrefix) { - final String account; - if (ProductionFlags.ENABLE_PER_ACCOUNT_USER_HISTORY_DICTIONARY) { - account = PreferenceManager.getDefaultSharedPreferences(context) - .getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null /* default */); - } else { - account = null; - } + final File dictFile, final String dictNamePrefix, @Nullable final String account) { return PersonalizationHelper.getUserHistoryDictionary(context, locale, account); } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 5f1a7af44..0669026d8 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -36,6 +36,7 @@ import java.util.Arrays; import java.util.Locale; import javax.annotation.Nonnull; +import javax.annotation.Nullable; /** * When you call the constructor of this class, you may want to change the current system locale by @@ -120,6 +121,8 @@ public class SettingsValues { public final float mKeyPreviewDismissEndXScale; public final float mKeyPreviewDismissEndYScale; + @Nullable public final String mAccount; + public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res, @Nonnull final InputAttributes inputAttributes) { mLocale = res.getConfiguration().locale; @@ -176,6 +179,8 @@ public class SettingsValues { mPlausibilityThreshold = Settings.readPlausibilityThreshold(res); mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res); mGestureTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true); + mAccount = prefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, + null /* default */); mGestureFloatingPreviewTextEnabled = !mInputAttributes.mDisableGestureFloatingPreviewText && prefs.getBoolean(Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true); mPhraseGestureEnabled = Settings.readPhraseGestureEnabled(prefs, res); diff --git a/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java index f07dac7c0..9d211c9e6 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin.personalization; import java.io.File; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -42,7 +43,7 @@ public class ContextualDictionaryTests extends AndroidTestCase { final DictionaryFacilitator dictionaryFacilitator = new DictionaryFacilitator(); dictionaryFacilitator.resetDictionariesForTesting(getContext(), new Locale[] { LOCALE_EN_US }, dictTypes, new HashMap(), - new HashMap>()); + Collections.>emptyMap(), null /* account */); return dictionaryFacilitator; } diff --git a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java index dc6fb0075..b133d61ab 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin.personalization; import java.io.File; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -57,7 +58,7 @@ public class PersonalizationDictionaryTests extends AndroidTestCase { final DictionaryFacilitator dictionaryFacilitator = new DictionaryFacilitator(getContext()); dictionaryFacilitator.resetDictionariesForTesting(getContext(), new Locale[] { LOCALE_EN_US }, dictTypes, new HashMap(), - new HashMap>()); + Collections.>emptyMap(), null /* account */); // Set subtypes. RichInputMethodManager.init(getContext()); final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java index d83c4a55b..6dddc971b 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java @@ -16,8 +16,6 @@ package com.android.inputmethod.latin.personalization; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; @@ -25,7 +23,6 @@ import android.util.Log; import com.android.inputmethod.latin.ExpandableBinaryDictionary; import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.NgramContext.WordInfo; -import com.android.inputmethod.latin.settings.LocalSettingsConstants; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; import com.android.inputmethod.latin.utils.DistracterFilter; @@ -35,8 +32,6 @@ import java.util.Locale; import java.util.Random; import java.util.concurrent.TimeUnit; -import javax.annotation.Nullable; - /** * Unit tests for UserHistoryDictionary */ @@ -48,9 +43,6 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { private int mCurrentTime = 0; - private SharedPreferences mPrefs; - private String mLastKnownAccount = null; - private static void printAllFiles(final File dir) { Log.d(TAG, dir.getAbsolutePath()); for (final File file : dir.listFiles()) { @@ -78,12 +70,6 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { @Override protected void setUp() throws Exception { super.setUp(); - - mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - // Keep track of the current account so that we restore it when the test finishes. - mLastKnownAccount = mPrefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null); - updateAccountName(TEST_ACCOUNT); - resetCurrentTimeForTestMode(); UserHistoryDictionaryTestsHelper.removeAllTestDictFiles( UserHistoryDictionaryTestsHelper.TEST_LOCALE_PREFIX, mContext); @@ -94,10 +80,6 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { UserHistoryDictionaryTestsHelper.removeAllTestDictFiles( UserHistoryDictionaryTestsHelper.TEST_LOCALE_PREFIX, mContext); stopTestModeInNativeCode(); - - // Restore the account that was present before running the test. - updateAccountName(mLastKnownAccount); - super.tearDown(); } @@ -106,14 +88,6 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { setCurrentTimeForTestMode(mCurrentTime); } - private void updateAccountName(@Nullable final String accountName) { - if (accountName == null) { - mPrefs.edit().remove(LocalSettingsConstants.PREF_ACCOUNT_NAME).apply(); - } else { - mPrefs.edit().putString(LocalSettingsConstants.PREF_ACCOUNT_NAME, accountName).apply(); - } - } - private void forcePassingShortTime() { // 3 days. final int timeToElapse = (int)TimeUnit.DAYS.toSeconds(3); @@ -147,17 +121,20 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { dict.waitAllTasksForTests(); } - public void testRandomWords() { + private void doTestRandomWords(final String testAccount) { Log.d(TAG, "This test can be used for profiling."); Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true."); final Locale dummyLocale = UserHistoryDictionaryTestsHelper.getDummyLocale("random_words"); final String dictName = UserHistoryDictionary.getUserHistoryDictName( - UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, getContext()); + UserHistoryDictionary.NAME, dummyLocale, + null /* dictFile */, + testAccount /* account */); final File dictFile = ExpandableBinaryDictionary.getDictFile( mContext, dictName, null /* dictFile */); final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( - getContext(), dummyLocale, TEST_ACCOUNT); + getContext(), dummyLocale, testAccount); clearHistory(dict); + final int numberOfWords = 1000; final Random random = new Random(123456); assertTrue(UserHistoryDictionaryTestsHelper.addAndWriteRandomWords( @@ -165,7 +142,23 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { assertDictionaryExists(dict, dictFile); } + public void testRandomWords_NullAccount() { + doTestRandomWords(null /* testAccount */); + } + + public void testRandomWords() { + doTestRandomWords(TEST_ACCOUNT); + } + public void testStressTestForSwitchingLanguagesAndAddingWords() { + doTestStressTestForSwitchingLanguagesAndAddingWords(TEST_ACCOUNT); + } + + public void testStressTestForSwitchingLanguagesAndAddingWords_NullAccount() { + doTestStressTestForSwitchingLanguagesAndAddingWords(null /* testAccount */); + } + + private void doTestStressTestForSwitchingLanguagesAndAddingWords(final String testAccount) { final int numberOfLanguages = 2; final int numberOfLanguageSwitching = 80; final int numberOfWordsInsertedForEachLanguageSwitch = 100; @@ -181,11 +174,12 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { final Locale dummyLocale = UserHistoryDictionaryTestsHelper.getDummyLocale("switching_languages" + i); final String dictName = UserHistoryDictionary.getUserHistoryDictName( - UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, getContext()); + UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, + testAccount /* account */); dictFiles[i] = ExpandableBinaryDictionary.getDictFile( mContext, dictName, null /* dictFile */); dicts[i] = PersonalizationHelper.getUserHistoryDictionary(getContext(), - dummyLocale, TEST_ACCOUNT); + dummyLocale, testAccount); clearHistory(dicts[i]); } @@ -212,16 +206,24 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { } public void testAddManyWords() { + doTestAddManyWords(TEST_ACCOUNT); + } + + public void testAddManyWords_NullAccount() { + doTestAddManyWords(null /* testAccount */); + } + + private void doTestAddManyWords(final String testAccount) { final Locale dummyLocale = UserHistoryDictionaryTestsHelper.getDummyLocale("many_random_words"); final String dictName = UserHistoryDictionary.getUserHistoryDictName( - UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, getContext()); + UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, testAccount); final File dictFile = ExpandableBinaryDictionary.getDictFile( mContext, dictName, null /* dictFile */); final int numberOfWords = 10000; final Random random = new Random(123456); final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( - getContext(), dummyLocale, TEST_ACCOUNT); + getContext(), dummyLocale, testAccount); clearHistory(dict); assertTrue(UserHistoryDictionaryTestsHelper.addAndWriteRandomWords(dict, numberOfWords, random, true /* checksContents */, mCurrentTime)); @@ -229,9 +231,17 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { } public void testDecaying() { + doTestDecaying(TEST_ACCOUNT); + } + + public void testDecaying_NullAccount() { + doTestDecaying(null /* testAccount */); + } + + private void doTestDecaying(final String testAccount) { final Locale dummyLocale = UserHistoryDictionaryTestsHelper.getDummyLocale("decaying"); final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( - getContext(), dummyLocale, TEST_ACCOUNT); + getContext(), dummyLocale, testAccount); resetCurrentTimeForTestMode(); clearHistory(dict); dict.waitAllTasksForTests(); @@ -262,9 +272,4 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { assertFalse(dict.isInDictionary(word)); } } - - public void testRandomWords_NullAccount() { - updateAccountName(null); - testRandomWords(); - } } \ No newline at end of file