Passing account info to dictionaryFacilitator

Attempt to use dictionary facilitor without invoking
preference manager. Instead use account from settings only when
things are being reset/changed. Discussion forked from ag/591663

Overall, the idea here is to maintain an account information
inside dictionary groups. Reset the dictionary groups if
account changes (the way we do for locale). Since only user
history dictionary is currently affected, the check to reset user
history dictionary also includes the check to verify the account.
For other things remain the same.

SettingsValues holds the current account (and is updated if prefs change
due to change in account settings). The updated settings are then
propagated to dictionary facilitator via LatinIME#loadSettings.

Bug:18104749,18469539
Change-Id: I553e776e7ea125d0fb7a1fe70a4c7eb0b2277fb8
main
Jatin Matani 2014-11-24 13:48:16 -08:00
parent 2c826fd28e
commit bc4ae6bdc0
14 changed files with 190 additions and 116 deletions

View File

@ -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);
}

View File

@ -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<String, ExpandableBinaryDictionary> mSubDictMap =
new ConcurrentHashMap<>();
public DictionaryGroup() {
mLocale = null;
this(null /* locale */, null /* mainDict */, null /* account */,
Collections.<String, ExpandableBinaryDictionary>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<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()) {
@ -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<? extends ExpandableBinaryDictionary> 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<Locale, ArrayList<String>> existingDictsToCleanup = new HashMap<>();
final String dictNamePrefix,
@Nullable final String account) {
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);
@ -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<String> dictsForLocale = new ArrayList<>();
existingDictsToCleanup.put(newLocale, dictsForLocale);
final ArrayList<String> 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<String> dictsToCleanupForLocale = existingDictsToCleanup.get(newLocale);
final ArrayList<String> 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<String, ExpandableBinaryDictionary> 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<String> 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<String> dictionaryTypes, final HashMap<String, File> dictionaryFiles,
final Map<String, Map<String, String>> additionalDictAttributes) {
final Map<String, Map<String, String>> additionalDictAttributes,
@Nullable final String account) {
Dictionary mainDictionary = null;
final Map<String, ExpandableBinaryDictionary> 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

View File

@ -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) {

View File

@ -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.

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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<String, File>(),
new HashMap<String, Map<String, String>>());
Collections.<String, Map<String, String>>emptyMap(), null /* account */);
return dictionaryFacilitator;
}

View File

@ -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<String, File>(),
new HashMap<String, Map<String, String>>());
Collections.<String, Map<String, String>>emptyMap(), null /* account */);
// Set subtypes.
RichInputMethodManager.init(getContext());
final RichInputMethodManager richImm = RichInputMethodManager.getInstance();

View File

@ -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();
}
}