am adfb2627: Remove logic related to dictionary loading from LatinIME.

* commit 'adfb262797023c4ca57bb470e547f90c88f638ca':
  Remove logic related to dictionary loading from LatinIME.
main
Keisuke Kuroyanagi 2014-03-24 23:46:24 -07:00 committed by Android Git Automerger
commit ab0f3721fa
8 changed files with 471 additions and 549 deletions

View File

@ -26,15 +26,14 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.personalization.PersonalizationDictionary;
import com.android.inputmethod.latin.personalization.PersonalizationHelper;
import com.android.inputmethod.latin.personalization.UserHistoryDictionary;
import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.ExecutorUtils;
import com.android.inputmethod.latin.utils.LanguageModelParam;
import com.android.inputmethod.latin.utils.SuggestionResults;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@ -50,197 +49,314 @@ public class DictionaryFacilitatorForSuggest {
// dictionary.
private static final int CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT = 140;
private final Context mContext;
public final Locale mLocale;
private Dictionaries mDictionaries = new Dictionaries();
private volatile CountDownLatch mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0);
// To synchronize assigning mDictionaries to ensure closing dictionaries.
private Object mLock = new Object();
private final ConcurrentHashMap<String, Dictionary> mDictionaries =
CollectionUtils.newConcurrentHashMap();
/**
* Class contains dictionaries for a locale.
*/
private static class Dictionaries {
public final Locale mLocale;
public final ConcurrentHashMap<String, Dictionary> mDictMap =
CollectionUtils.newConcurrentHashMap();
// Main dictionary will be asynchronously loaded.
public Dictionary mMainDictionary;
public final ContactsBinaryDictionary mContactsDictionary;
public final UserBinaryDictionary mUserDictionary;
public final UserHistoryDictionary mUserHistoryDictionary;
public final PersonalizationDictionary mPersonalizationDictionary;
private Dictionary mMainDictionary;
private ContactsBinaryDictionary mContactsDictionary;
private UserBinaryDictionary mUserDictionary;
private UserHistoryDictionary mUserHistoryDictionary;
private PersonalizationDictionary mPersonalizationDictionary;
public Dictionaries() {
mLocale = null;
mMainDictionary = null;
mContactsDictionary = null;
mUserDictionary = null;
mUserHistoryDictionary = null;
mPersonalizationDictionary = null;
}
private final CountDownLatch mLatchForWaitingLoadingMainDictionary;
public Dictionaries(final Locale locale, final Dictionary mainDict,
final ContactsBinaryDictionary contactsDict, final UserBinaryDictionary userDict,
final UserHistoryDictionary userHistoryDict,
final PersonalizationDictionary personalizationDict) {
mLocale = locale;
setMainDict(mainDict);
mContactsDictionary = contactsDict;
if (mContactsDictionary != null) {
mDictMap.put(Dictionary.TYPE_CONTACTS, mContactsDictionary);
}
mUserDictionary = userDict;
if (mUserDictionary != null) {
mDictMap.put(Dictionary.TYPE_USER, mUserDictionary);
}
mUserHistoryDictionary = userHistoryDict;
if (mUserHistoryDictionary != null) {
mDictMap.put(Dictionary.TYPE_USER_HISTORY, mUserHistoryDictionary);
}
mPersonalizationDictionary = personalizationDict;
if (mPersonalizationDictionary != null) {
mDictMap.put(Dictionary.TYPE_PERSONALIZATION, mPersonalizationDictionary);
}
}
public void setMainDict(final Dictionary mainDict) {
mMainDictionary = mainDict;
// Close old dictionary if exists. Main dictionary can be assigned multiple times.
final Dictionary oldDict;
if (mMainDictionary != null) {
oldDict = mDictMap.put(Dictionary.TYPE_MAIN, mMainDictionary);
} else {
oldDict = mDictMap.remove(Dictionary.TYPE_MAIN);
}
if (oldDict != null && mMainDictionary != oldDict) {
oldDict.close();
}
}
public boolean hasMainDict() {
return mMainDictionary != null;
}
public boolean hasContactsDict() {
return mContactsDictionary != null;
}
public boolean hasUserDict() {
return mUserDictionary != null;
}
public boolean hasUserHistoryDict() {
return mUserHistoryDictionary != null;
}
public boolean hasPersonalizationDict() {
return mPersonalizationDictionary != null;
}
}
public interface DictionaryInitializationListener {
public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable);
}
/**
* Creates instance for initialization or when the locale is changed.
*
* @param context the context
* @param locale the locale
* @param settingsValues current settings values to control what dictionaries should be used
* @param listener the listener
* @param oldDictionaryFacilitator the instance having old dictionaries. This is null when the
* instance is initially created.
*/
public DictionaryFacilitatorForSuggest(final Context context, final Locale locale,
final SettingsValues settingsValues, final DictionaryInitializationListener listener,
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator) {
mContext = context;
mLocale = locale;
mLatchForWaitingLoadingMainDictionary = new CountDownLatch(1);
loadMainDict(context, locale, listener);
setUserDictionary(new UserBinaryDictionary(context, locale));
resetAdditionalDictionaries(oldDictionaryFacilitator, settingsValues);
public DictionaryFacilitatorForSuggest() {}
public Locale getLocale() {
return mDictionaries.mLocale;
}
/**
* Creates instance for reloading the main dict.
*
* @param listener the listener
* @param oldDictionaryFacilitator the instance having old dictionaries. This must not be null.
*/
public DictionaryFacilitatorForSuggest(final DictionaryInitializationListener listener,
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator) {
mContext = oldDictionaryFacilitator.mContext;
mLocale = oldDictionaryFacilitator.mLocale;
mLatchForWaitingLoadingMainDictionary = new CountDownLatch(1);
loadMainDict(mContext, mLocale, listener);
// Transfer user dictionary.
setUserDictionary(oldDictionaryFacilitator.mUserDictionary);
oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_USER);
// Transfer contacts dictionary.
setContactsDictionary(oldDictionaryFacilitator.mContactsDictionary);
oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_CONTACTS);
// Transfer user history dictionary.
setUserHistoryDictionary(oldDictionaryFacilitator.mUserHistoryDictionary);
oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_USER_HISTORY);
// Transfer personalization dictionary.
setPersonalizationDictionary(oldDictionaryFacilitator.mPersonalizationDictionary);
oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_PERSONALIZATION);
public void resetDictionaries(final Context context, final Locale newLocale,
final boolean useContactsDict, final boolean usePersonalizedDicts,
final boolean forceReloadMainDictionary,
final DictionaryInitializationListener listener) {
final boolean localeHasBeenChanged = !newLocale.equals(mDictionaries.mLocale);
// We always try to have the main dictionary. Other dictionaries can be unused.
final boolean reloadMainDictionary = localeHasBeenChanged || forceReloadMainDictionary;
final boolean closeContactsDictionary = localeHasBeenChanged || !useContactsDict;
final boolean closeUserDictionary = localeHasBeenChanged;
final boolean closeUserHistoryDictionary = localeHasBeenChanged || !usePersonalizedDicts;
final boolean closePersonalizationDictionary =
localeHasBeenChanged || !usePersonalizedDicts;
final Dictionary newMainDict;
if (reloadMainDictionary) {
// The main dictionary will be asynchronously loaded.
newMainDict = null;
} else {
newMainDict = mDictionaries.mMainDictionary;
}
// Open or move contacts dictionary.
final ContactsBinaryDictionary newContactsDict;
if (!closeContactsDictionary && mDictionaries.hasContactsDict()) {
newContactsDict = mDictionaries.mContactsDictionary;
} else if (useContactsDict) {
newContactsDict = new ContactsBinaryDictionary(context, newLocale);
} else {
newContactsDict = null;
}
// Open or move user dictionary.
final UserBinaryDictionary newUserDictionary;
if (!closeUserDictionary && mDictionaries.hasUserDict()) {
newUserDictionary = mDictionaries.mUserDictionary;
} else {
newUserDictionary = new UserBinaryDictionary(context, newLocale);
}
// Open or move user history dictionary.
final UserHistoryDictionary newUserHistoryDict;
if (!closeUserHistoryDictionary && mDictionaries.hasUserHistoryDict()) {
newUserHistoryDict = mDictionaries.mUserHistoryDictionary;
} else if (usePersonalizedDicts) {
newUserHistoryDict = PersonalizationHelper.getUserHistoryDictionary(context, newLocale);
} else {
newUserHistoryDict = null;
}
// Open or move personalization dictionary.
final PersonalizationDictionary newPersonalizationDict;
if (!closePersonalizationDictionary && mDictionaries.hasPersonalizationDict()) {
newPersonalizationDict = mDictionaries.mPersonalizationDictionary;
} else if (usePersonalizedDicts) {
newPersonalizationDict =
PersonalizationHelper.getPersonalizationDictionary(context, newLocale);
} else {
newPersonalizationDict = null;
}
// Replace Dictionaries.
final Dictionaries newDictionaries = new Dictionaries(newLocale, newMainDict,
newContactsDict, newUserDictionary, newUserHistoryDict, newPersonalizationDict);
if (listener != null) {
listener.onUpdateMainDictionaryAvailability(newDictionaries.hasMainDict());
}
final Dictionaries oldDictionaries;
synchronized (mLock) {
oldDictionaries = mDictionaries;
mDictionaries = newDictionaries;
if (reloadMainDictionary) {
asyncReloadMainDictionary(context, newLocale, listener);
}
}
// Clean up old dictionaries.
oldDictionaries.mDictMap.clear();
if (reloadMainDictionary && oldDictionaries.hasMainDict()) {
oldDictionaries.mMainDictionary.close();
}
if (closeContactsDictionary && oldDictionaries.hasContactsDict()) {
oldDictionaries.mContactsDictionary.close();
}
if (closeUserDictionary && oldDictionaries.hasUserDict()) {
oldDictionaries.mUserDictionary.close();
}
if (closeUserHistoryDictionary && oldDictionaries.hasUserHistoryDict()) {
oldDictionaries.mUserHistoryDictionary.close();
}
if (closePersonalizationDictionary && oldDictionaries.hasPersonalizationDict()) {
oldDictionaries.mPersonalizationDictionary.close();
}
}
/**
* Creates instance for when the settings values have been changed.
*
* @param settingsValues the new settings values
* @param oldDictionaryFacilitator the instance having old dictionaries. This must not be null.
*/
//
public DictionaryFacilitatorForSuggest(final SettingsValues settingsValues,
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator) {
mContext = oldDictionaryFacilitator.mContext;
mLocale = oldDictionaryFacilitator.mLocale;
mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0);
// Transfer main dictionary.
setMainDictionary(oldDictionaryFacilitator.mMainDictionary);
oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_MAIN);
// Transfer user dictionary.
setUserDictionary(oldDictionaryFacilitator.mUserDictionary);
oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_USER);
// Transfer or create additional dictionaries depending on the settings values.
resetAdditionalDictionaries(oldDictionaryFacilitator, settingsValues);
private void asyncReloadMainDictionary(final Context context, final Locale locale,
final DictionaryInitializationListener listener) {
final CountDownLatch latchForWaitingLoadingMainDictionary = new CountDownLatch(1);
mLatchForWaitingLoadingMainDictionary = latchForWaitingLoadingMainDictionary;
ExecutorUtils.getExecutor("InitializeBinaryDictionary").execute(new Runnable() {
@Override
public void run() {
final Dictionary mainDict =
DictionaryFactory.createMainDictionaryFromManager(context, locale);
synchronized (mLock) {
if (locale.equals(mDictionaries.mLocale)) {
mDictionaries.setMainDict(mainDict);
} else {
// Dictionary facilitator has been reset for another locale.
mainDict.close();
}
}
if (listener != null) {
listener.onUpdateMainDictionaryAvailability(mDictionaries.hasMainDict());
}
latchForWaitingLoadingMainDictionary.countDown();
}
});
}
@UsedForTesting
public DictionaryFacilitatorForSuggest(final Context context, final Locale locale,
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) {
mContext = context;
mLocale = locale;
mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0);
Dictionary mainDictionary = null;
ContactsBinaryDictionary contactsDictionary = null;
UserBinaryDictionary userDictionary = null;
UserHistoryDictionary userHistoryDictionary = null;
PersonalizationDictionary personalizationDictionary = null;
for (final String dictType : dictionaryTypes) {
if (dictType.equals(Dictionary.TYPE_MAIN)) {
final DictionaryCollection mainDictionary =
DictionaryFactory.createMainDictionaryFromManager(context, locale);
setMainDictionary(mainDictionary);
mainDictionary = DictionaryFactory.createMainDictionaryFromManager(context, locale);
} else if (dictType.equals(Dictionary.TYPE_USER_HISTORY)) {
final UserHistoryDictionary userHistoryDictionary =
userHistoryDictionary =
PersonalizationHelper.getUserHistoryDictionary(context, locale);
// Staring with an empty user history dictionary for testing.
// Testing program may populate this dictionary before actual testing.
userHistoryDictionary.reloadDictionaryIfRequired();
userHistoryDictionary.waitAllTasksForTests();
setUserHistoryDictionary(userHistoryDictionary);
if (additionalDictAttributes.containsKey(dictType)) {
userHistoryDictionary.clearAndFlushDictionaryWithAdditionalAttributes(
additionalDictAttributes.get(dictType));
}
} else if (dictType.equals(Dictionary.TYPE_PERSONALIZATION)) {
final PersonalizationDictionary personalizationDictionary =
personalizationDictionary =
PersonalizationHelper.getPersonalizationDictionary(context, locale);
// Staring with an empty personalization dictionary for testing.
// Testing program may populate this dictionary before actual testing.
personalizationDictionary.reloadDictionaryIfRequired();
personalizationDictionary.waitAllTasksForTests();
setPersonalizationDictionary(personalizationDictionary);
if (additionalDictAttributes.containsKey(dictType)) {
personalizationDictionary.clearAndFlushDictionaryWithAdditionalAttributes(
additionalDictAttributes.get(dictType));
}
} else if (dictType.equals(Dictionary.TYPE_USER)) {
final File file = dictionaryFiles.get(dictType);
final UserBinaryDictionary userDictionary = new UserBinaryDictionary(
context, locale, file);
userDictionary = new UserBinaryDictionary(context, locale, file);
userDictionary.reloadDictionaryIfRequired();
userDictionary.waitAllTasksForTests();
setUserDictionary(userDictionary);
} else if (dictType.equals(Dictionary.TYPE_CONTACTS)) {
final File file = dictionaryFiles.get(dictType);
final ContactsBinaryDictionary contactsDictionary = new ContactsBinaryDictionary(
context, locale, file);
contactsDictionary = new ContactsBinaryDictionary(context, locale, file);
contactsDictionary.reloadDictionaryIfRequired();
contactsDictionary.waitAllTasksForTests();
setContactsDictionary(contactsDictionary);
} else {
throw new RuntimeException("Unknown dictionary type: " + dictType);
}
}
mDictionaries = new Dictionaries(locale, mainDictionary, contactsDictionary,
userDictionary, userHistoryDictionary, personalizationDictionary);
}
public boolean needsToBeRecreated(final Locale newLocale,
final SettingsValues newSettingsValues) {
return !mLocale.equals(newLocale)
|| (newSettingsValues.mUseContactsDict != (mContactsDictionary != null))
|| (newSettingsValues.mUsePersonalizedDicts != (mUserHistoryDictionary != null))
|| (newSettingsValues.mUsePersonalizedDicts != hasPersonalizationDictionary());
}
public void close() {
final HashSet<Dictionary> dictionaries = CollectionUtils.newHashSet();
dictionaries.addAll(mDictionaries.values());
for (final Dictionary dictionary : dictionaries) {
dictionary.close();
public void closeDictionaries() {
final Dictionaries dictionaries;
synchronized (mLock) {
dictionaries = mDictionaries;
mDictionaries = new Dictionaries();
}
}
private void loadMainDict(final Context context, final Locale locale,
final DictionaryInitializationListener listener) {
mMainDictionary = null;
if (listener != null) {
listener.onUpdateMainDictionaryAvailability(hasMainDictionary());
if (dictionaries.hasMainDict()) {
dictionaries.mMainDictionary.close();
}
if (dictionaries.hasContactsDict()) {
dictionaries.mContactsDictionary.close();
}
if (dictionaries.hasUserDict()) {
dictionaries.mUserDictionary.close();
}
if (dictionaries.hasUserHistoryDict()) {
dictionaries.mUserHistoryDictionary.close();
}
if (dictionaries.hasPersonalizationDict()) {
dictionaries.mPersonalizationDictionary.close();
}
ExecutorUtils.getExecutor("InitializeBinaryDictionary").execute(new Runnable() {
public void run() {
final DictionaryCollection newMainDict =
DictionaryFactory.createMainDictionaryFromManager(context, locale);
setMainDictionary(newMainDict);
if (listener != null) {
listener.onUpdateMainDictionaryAvailability(hasMainDictionary());
}
mLatchForWaitingLoadingMainDictionary.countDown();
}
});
}
// The main dictionary could have been loaded asynchronously. Don't cache the return value
// of this method.
public boolean hasMainDictionary() {
return null != mMainDictionary && mMainDictionary.isInitialized();
public boolean hasInitializedMainDictionary() {
final Dictionaries dictionaries = mDictionaries;
return dictionaries.hasMainDict() && dictionaries.mMainDictionary.isInitialized();
}
public boolean hasPersonalizationDictionary() {
return null != mPersonalizationDictionary;
return mDictionaries.hasPersonalizationDict();
}
public void flushPersonalizationDictionary() {
if (hasPersonalizationDictionary()) {
mPersonalizationDictionary.flush();
final PersonalizationDictionary personalizationDict =
mDictionaries.mPersonalizationDictionary;
if (personalizationDict != null) {
personalizationDict.flush();
}
}
@ -253,177 +369,48 @@ public class DictionaryFacilitatorForSuggest {
public void waitForLoadingDictionariesForTesting(final long timeout, final TimeUnit unit)
throws InterruptedException {
waitForLoadingMainDictionary(timeout, unit);
if (mContactsDictionary != null) {
mContactsDictionary.waitAllTasksForTests();
final Dictionaries dictionaries = mDictionaries;
if (dictionaries.hasContactsDict()) {
dictionaries.mContactsDictionary.waitAllTasksForTests();
}
if (mUserDictionary != null) {
mUserDictionary.waitAllTasksForTests();
if (dictionaries.hasUserDict()) {
dictionaries.mUserDictionary.waitAllTasksForTests();
}
if (mUserHistoryDictionary != null) {
mUserHistoryDictionary.waitAllTasksForTests();
if (dictionaries.hasUserHistoryDict()) {
dictionaries.mUserHistoryDictionary.waitAllTasksForTests();
}
if (mPersonalizationDictionary != null) {
mPersonalizationDictionary.waitAllTasksForTests();
if (dictionaries.hasPersonalizationDict()) {
dictionaries.mPersonalizationDictionary.waitAllTasksForTests();
}
}
private void setMainDictionary(final Dictionary mainDictionary) {
mMainDictionary = mainDictionary;
addOrReplaceDictionary(Dictionary.TYPE_MAIN, mainDictionary);
}
/**
* Sets an optional user dictionary resource to be loaded. The user dictionary is consulted
* before the main dictionary, if set. This refers to the system-managed user dictionary.
*/
private void setUserDictionary(final UserBinaryDictionary userDictionary) {
mUserDictionary = userDictionary;
addOrReplaceDictionary(Dictionary.TYPE_USER, userDictionary);
}
/**
* Sets an optional contacts dictionary resource to be loaded. It is also possible to remove
* the contacts dictionary by passing null to this method. In this case no contacts dictionary
* won't be used.
*/
private void setContactsDictionary(final ContactsBinaryDictionary contactsDictionary) {
mContactsDictionary = contactsDictionary;
addOrReplaceDictionary(Dictionary.TYPE_CONTACTS, contactsDictionary);
}
private void setUserHistoryDictionary(final UserHistoryDictionary userHistoryDictionary) {
mUserHistoryDictionary = userHistoryDictionary;
addOrReplaceDictionary(Dictionary.TYPE_USER_HISTORY, userHistoryDictionary);
}
private void setPersonalizationDictionary(
final PersonalizationDictionary personalizationDictionary) {
mPersonalizationDictionary = personalizationDictionary;
addOrReplaceDictionary(Dictionary.TYPE_PERSONALIZATION, personalizationDictionary);
}
/**
* Reset dictionaries that can be turned off according to the user settings.
*
* @param oldDictionaryFacilitator the instance having old dictionaries
* @param settingsValues current SettingsValues
*/
private void resetAdditionalDictionaries(
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator,
final SettingsValues settingsValues) {
// Contacts dictionary
resetContactsDictionary(null != oldDictionaryFacilitator ?
oldDictionaryFacilitator.mContactsDictionary : null, settingsValues);
// User history dictionary & Personalization dictionary
resetPersonalizedDictionaries(oldDictionaryFacilitator, settingsValues);
}
/**
* Set the user history dictionary and personalization dictionary according to the user
* settings.
*
* @param oldDictionaryFacilitator the instance that has been used
* @param settingsValues current settingsValues
*/
// TODO: Consolidate resetPersonalizedDictionaries() and resetContactsDictionary(). Call up the
// new method for each dictionary.
private void resetPersonalizedDictionaries(
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator,
final SettingsValues settingsValues) {
final boolean shouldSetDictionaries = settingsValues.mUsePersonalizedDicts;
final UserHistoryDictionary oldUserHistoryDictionary = (null == oldDictionaryFacilitator) ?
null : oldDictionaryFacilitator.mUserHistoryDictionary;
final PersonalizationDictionary oldPersonalizationDictionary =
(null == oldDictionaryFacilitator) ? null :
oldDictionaryFacilitator.mPersonalizationDictionary;
final UserHistoryDictionary userHistoryDictionaryToUse;
final PersonalizationDictionary personalizationDictionaryToUse;
if (!shouldSetDictionaries) {
userHistoryDictionaryToUse = null;
personalizationDictionaryToUse = null;
} else {
if (null != oldUserHistoryDictionary
&& oldUserHistoryDictionary.mLocale.equals(mLocale)) {
userHistoryDictionaryToUse = oldUserHistoryDictionary;
} else {
userHistoryDictionaryToUse =
PersonalizationHelper.getUserHistoryDictionary(mContext, mLocale);
}
if (null != oldPersonalizationDictionary
&& oldPersonalizationDictionary.mLocale.equals(mLocale)) {
personalizationDictionaryToUse = oldPersonalizationDictionary;
} else {
personalizationDictionaryToUse =
PersonalizationHelper.getPersonalizationDictionary(mContext, mLocale);
}
}
setUserHistoryDictionary(userHistoryDictionaryToUse);
setPersonalizationDictionary(personalizationDictionaryToUse);
}
/**
* Set the contacts dictionary according to the user settings.
*
* This method takes an optional contacts dictionary to use when the locale hasn't changed
* since the contacts dictionary can be opened or closed as necessary depending on the settings.
*
* @param oldContactsDictionary an optional dictionary to use, or null
* @param settingsValues current settingsValues
*/
private void resetContactsDictionary(final ContactsBinaryDictionary oldContactsDictionary,
final SettingsValues settingsValues) {
final boolean shouldSetDictionary = settingsValues.mUseContactsDict;
final ContactsBinaryDictionary dictionaryToUse;
if (!shouldSetDictionary) {
// Make sure the dictionary is closed. If it is already closed, this is a no-op,
// so it's safe to call it anyways.
if (null != oldContactsDictionary) oldContactsDictionary.close();
dictionaryToUse = null;
} else {
if (null != oldContactsDictionary) {
if (!oldContactsDictionary.mLocale.equals(mLocale)) {
// If the locale has changed then recreate the contacts dictionary. This
// allows locale dependent rules for handling bigram name predictions.
oldContactsDictionary.close();
dictionaryToUse = new ContactsBinaryDictionary(mContext, mLocale);
} else {
// Make sure the old contacts dictionary is opened. If it is already open,
// this is a no-op, so it's safe to call it anyways.
oldContactsDictionary.reopen(mContext);
dictionaryToUse = oldContactsDictionary;
}
} else {
dictionaryToUse = new ContactsBinaryDictionary(mContext, mLocale);
}
}
setContactsDictionary(dictionaryToUse);
}
public boolean isUserDictionaryEnabled() {
if (mUserDictionary == null) {
final UserBinaryDictionary userDictionary = mDictionaries.mUserDictionary;
if (userDictionary == null) {
return false;
}
return mUserDictionary.mEnabled;
return userDictionary.mEnabled;
}
public void addWordToUserDictionary(String word) {
if (mUserDictionary == null) {
final UserBinaryDictionary userDictionary = mDictionaries.mUserDictionary;
if (userDictionary == null) {
return;
}
mUserDictionary.addWordToUserDictionary(word);
userDictionary.addWordToUserDictionary(word);
}
public void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized,
final String previousWord, final int timeStampInSeconds) {
if (mUserHistoryDictionary == null) {
final Dictionaries dictionaries = mDictionaries;
if (!dictionaries.hasUserHistoryDict()) {
return;
}
final int maxFreq = getMaxFrequency(suggestion);
if (maxFreq == 0) {
return;
}
final String suggestionLowerCase = suggestion.toLowerCase(mLocale);
final String suggestionLowerCase = suggestion.toLowerCase(dictionaries.mLocale);
final String secondWord;
if (wasAutoCapitalized) {
secondWord = suggestionLowerCase;
@ -432,11 +419,11 @@ public class DictionaryFacilitatorForSuggest {
// History dictionary in order to avoid suggesting them until the dictionary
// consolidation is done.
// TODO: Remove this hack when ready.
final int lowerCasefreqInMainDict = mMainDictionary != null ?
mMainDictionary.getFrequency(suggestionLowerCase) :
final int lowerCaseFreqInMainDict = dictionaries.hasMainDict() ?
dictionaries.mMainDictionary.getFrequency(suggestionLowerCase) :
Dictionary.NOT_A_PROBABILITY;
if (maxFreq < lowerCasefreqInMainDict
&& lowerCasefreqInMainDict >= CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT) {
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 = suggestionLowerCase;
} else {
@ -446,54 +433,56 @@ public class DictionaryFacilitatorForSuggest {
// 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;
mUserHistoryDictionary.addToDictionary(
dictionaries.mUserHistoryDictionary.addToDictionary(
previousWord, secondWord, isValid, timeStampInSeconds);
}
public void cancelAddingUserHistory(final String previousWord, final String committedWord) {
if (mUserHistoryDictionary != null) {
mUserHistoryDictionary.cancelAddingUserHistory(previousWord, committedWord);
final UserHistoryDictionary userHistoryDictionary = mDictionaries.mUserHistoryDictionary;
if (userHistoryDictionary != null) {
userHistoryDictionary.cancelAddingUserHistory(previousWord, committedWord);
}
}
// TODO: Revise the way to fusion suggestion results.
public void getSuggestions(final WordComposer composer,
public SuggestionResults getSuggestionResults(final WordComposer composer,
final String prevWord, final ProximityInfo proximityInfo,
final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
final int sessionId, final Set<SuggestedWordInfo> suggestionSet,
final ArrayList<SuggestedWordInfo> rawSuggestions) {
for (final String key : mDictionaries.keySet()) {
final Dictionary dictionary = mDictionaries.get(key);
final int sessionId, final ArrayList<SuggestedWordInfo> rawSuggestions) {
final Dictionaries dictionaries = mDictionaries;
final Map<String, Dictionary> dictMap = dictionaries.mDictMap;
final SuggestionResults suggestionResults =
new SuggestionResults(dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS);
for (final Dictionary dictionary : dictMap.values()) {
if (null == dictionary) continue;
final ArrayList<SuggestedWordInfo> dictionarySuggestions =
dictionary.getSuggestionsWithSessionId(composer, prevWord, proximityInfo,
blockOffensiveWords, additionalFeaturesOptions, sessionId);
if (null == dictionarySuggestions) continue;
suggestionSet.addAll(dictionarySuggestions);
suggestionResults.addAll(dictionarySuggestions);
if (null != rawSuggestions) {
rawSuggestions.addAll(dictionarySuggestions);
}
}
return suggestionResults;
}
public boolean isValidMainDictWord(final String word) {
if (TextUtils.isEmpty(word) || !hasMainDictionary()) {
final Dictionaries dictionaries = mDictionaries;
if (TextUtils.isEmpty(word) || !dictionaries.hasMainDict()) {
return false;
}
return mMainDictionary.isValidWord(word);
return dictionaries.mMainDictionary.isValidWord(word);
}
public boolean isValidWord(final String word, final boolean ignoreCase) {
if (TextUtils.isEmpty(word)) {
return false;
}
final String lowerCasedWord = word.toLowerCase(mLocale);
for (final String key : mDictionaries.keySet()) {
final Dictionary dictionary = mDictionaries.get(key);
// It's unclear how realistically 'dictionary' can be null, but the monkey is somehow
// managing to get null in here. Presumably the language is changing to a language with
// no main dictionary and the monkey manages to type a whole word before the thread
// that reads the dictionary is started or something?
final Dictionaries dictionaries = mDictionaries;
final String lowerCasedWord = word.toLowerCase(dictionaries.mLocale);
final Map<String, Dictionary> dictMap = dictionaries.mDictMap;
for (final Dictionary dictionary : dictMap.values()) {
// 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.
@ -511,9 +500,8 @@ public class DictionaryFacilitatorForSuggest {
return Dictionary.NOT_A_PROBABILITY;
}
int maxFreq = -1;
for (final String key : mDictionaries.keySet()) {
final Dictionary dictionary = mDictionaries.get(key);
if (null == dictionary) continue;
final Map<String, Dictionary> dictMap = mDictionaries.mDictMap;
for (final Dictionary dictionary : dictMap.values()) {
final int tempFreq = dictionary.getFrequency(word);
if (tempFreq >= maxFreq) {
maxFreq = tempFreq;
@ -522,61 +510,50 @@ public class DictionaryFacilitatorForSuggest {
return maxFreq;
}
private void removeDictionary(final String key) {
mDictionaries.remove(key);
}
private void addOrReplaceDictionary(final String key, final Dictionary dict) {
final Dictionary oldDict;
if (dict == null) {
oldDict = mDictionaries.remove(key);
} else {
oldDict = mDictionaries.put(key, dict);
}
if (oldDict != null && dict != oldDict) {
oldDict.close();
}
}
public void clearUserHistoryDictionary() {
if (mUserHistoryDictionary == null) {
final UserHistoryDictionary userHistoryDict = mDictionaries.mUserHistoryDictionary;
if (userHistoryDict == null) {
return;
}
mUserHistoryDictionary.clearAndFlushDictionary();
userHistoryDict.clearAndFlushDictionary();
}
// This method gets called only when the IME receives a notification to remove the
// personalization dictionary.
public void clearPersonalizationDictionary() {
if (!hasPersonalizationDictionary()) {
final PersonalizationDictionary personalizationDict =
mDictionaries.mPersonalizationDictionary;
if (personalizationDict == null) {
return;
}
mPersonalizationDictionary.clearAndFlushDictionary();
personalizationDict.clearAndFlushDictionary();
}
public void addMultipleDictionaryEntriesToPersonalizationDictionary(
final ArrayList<LanguageModelParam> languageModelParams,
final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) {
if (!hasPersonalizationDictionary()) {
final PersonalizationDictionary personalizationDict =
mDictionaries.mPersonalizationDictionary;
if (personalizationDict == null) {
if (callback != null) {
callback.onFinished();
}
return;
}
mPersonalizationDictionary.addMultipleDictionaryEntriesToDictionary(languageModelParams,
callback);
personalizationDict.addMultipleDictionaryEntriesToDictionary(languageModelParams, callback);
}
public void dumpDictionaryForDebug(final String dictName) {
final ExpandableBinaryDictionary dictToDump;
if (dictName.equals(Dictionary.TYPE_CONTACTS)) {
dictToDump = mContactsDictionary;
dictToDump = mDictionaries.mContactsDictionary;
} else if (dictName.equals(Dictionary.TYPE_USER)) {
dictToDump = mUserDictionary;
dictToDump = mDictionaries.mUserDictionary;
} else if (dictName.equals(Dictionary.TYPE_USER_HISTORY)) {
dictToDump = mUserHistoryDictionary;
dictToDump = mDictionaries.mUserHistoryDictionary;
} else if (dictName.equals(Dictionary.TYPE_PERSONALIZATION)) {
dictToDump = mPersonalizationDictionary;
dictToDump = mDictionaries.mPersonalizationDictionary;
} else {
dictToDump = null;
}

View File

@ -215,7 +215,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
false /* includeResumedWordInSuggestions */);
break;
case MSG_REOPEN_DICTIONARIES:
latinIme.initSuggest();
latinIme.resetSuggest();
// In theory we could call latinIme.updateSuggestionStrip() right away, but
// in the practice, the dictionary is not finished opening yet so we wouldn't
// get any suggestions. Wait one frame.
@ -478,10 +478,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// TODO: Resolve mutual dependencies of {@link #loadSettings()} and {@link #initSuggest()}.
loadSettings();
initSuggest();
resetSuggest();
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.getInstance().init(this, mKeyboardSwitcher);
ResearchLogger.getInstance().initDictionary(
mInputLogic.mSuggest.mDictionaryFacilitator);
}
// Register to receive ringer mode change and network state change.
@ -520,31 +522,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// This method is called on startup and language switch, before the new layout has
// been displayed. Opening dictionaries never affects responsivity as dictionaries are
// asynchronously loaded.
initOrResetSuggestForSettingsValues(mInputLogic.mSuggest, locale, currentSettingsValues);
}
private void initOrResetSuggestForSettingsValues(final Suggest oldSuggest,
final Locale locale, final SettingsValues settingsValues) {
if (!mHandler.hasPendingReopenDictionaries() && oldSuggest != null) {
// May need to reset dictionaries depending on the user settings.
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator =
oldSuggest.mDictionaryFacilitator;
if (!oldDictionaryFacilitator.needsToBeRecreated(locale, settingsValues)) {
// Continue to use the same dictionary facilitator if no configuration has changed.
refreshPersonalizationDictionarySession();
return;
}
final DictionaryFacilitatorForSuggest dictionaryFacilitator =
new DictionaryFacilitatorForSuggest(settingsValues, oldDictionaryFacilitator);
// Create Suggest instance with the new dictionary facilitator.
replaceSuggest(new Suggest(oldSuggest, dictionaryFacilitator));
} else if (oldSuggest == null) {
initSuggest();
if (!mHandler.hasPendingReopenDictionaries()) {
resetSuggestForLocale(locale);
}
refreshPersonalizationDictionarySession();
}
private void refreshPersonalizationDictionarySession() {
final Suggest suggest = mInputLogic.mSuggest;
final DictionaryFacilitatorForSuggest dictionaryFacilitator =
mInputLogic.mSuggest.mDictionaryFacilitator;
final boolean shouldKeepUserHistoryDictionaries;
final boolean shouldKeepPersonalizationDictionaries;
if (mSettings.getCurrent().mUsePersonalizedDicts) {
@ -559,17 +545,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (!shouldKeepUserHistoryDictionaries) {
// Remove user history dictionaries.
PersonalizationHelper.removeAllUserHistoryDictionaries(this);
if (suggest != null) {
suggest.mDictionaryFacilitator.clearUserHistoryDictionary();
}
dictionaryFacilitator.clearUserHistoryDictionary();
}
if (!shouldKeepPersonalizationDictionaries) {
// Remove personalization dictionaries.
PersonalizationHelper.removeAllPersonalizationDictionaries(this);
PersonalizationDictionarySessionRegistrar.resetAll(this);
} else {
final DictionaryFacilitatorForSuggest dictionaryFacilitator =
(suggest == null) ? null : suggest.mDictionaryFacilitator;
PersonalizationDictionarySessionRegistrar.init(this, dictionaryFacilitator);
}
}
@ -583,7 +565,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
private void initSuggest() {
private void resetSuggest() {
final Locale switcherSubtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
final String switcherLocaleStr = switcherSubtypeLocale.toString();
final Locale subtypeLocale;
@ -599,47 +581,42 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
subtypeLocale = switcherSubtypeLocale;
}
initSuggestForLocale(mInputLogic.mSuggest, subtypeLocale);
resetSuggestForLocale(subtypeLocale);
}
private void initSuggestForLocale(final Suggest oldSuggest, final Locale locale) {
final SettingsValues settingsValues = mSettings.getCurrent();
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator =
(oldSuggest == null) ? null : oldSuggest.mDictionaryFacilitator;
// Creates new dictionary facilitator for the new locale.
/**
* Reset suggest by loading dictionaries for the locale and the current settings values.
*
* @param locale the locale
*/
private void resetSuggestForLocale(final Locale locale) {
final DictionaryFacilitatorForSuggest dictionaryFacilitator =
new DictionaryFacilitatorForSuggest(this /* context */, locale, settingsValues,
this /* DictionaryInitializationListener */, oldDictionaryFacilitator);
final Suggest newSuggest = new Suggest(locale, dictionaryFacilitator);
if (settingsValues.mCorrectionEnabled) {
newSuggest.setAutoCorrectionThreshold(settingsValues.mAutoCorrectionThreshold);
}
replaceSuggest(newSuggest);
}
/* package private */ void resetSuggestMainDict() {
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator =
mInputLogic.mSuggest.mDictionaryFacilitator;
final DictionaryFacilitatorForSuggest dictionaryFacilitator =
new DictionaryFacilitatorForSuggest(this /* listener */, oldDictionaryFacilitator);
replaceSuggest(new Suggest(mInputLogic.mSuggest /* oldSuggest */, dictionaryFacilitator));
final SettingsValues settingsValues = mSettings.getCurrent();
dictionaryFacilitator.resetDictionaries(this /* context */, locale,
settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts,
false /* forceReloadMainDictionary */, this);
if (settingsValues.mCorrectionEnabled) {
mInputLogic.mSuggest.setAutoCorrectionThreshold(
settingsValues.mAutoCorrectionThreshold);
}
}
private void replaceSuggest(final Suggest newSuggest) {
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.getInstance().initDictionary(newSuggest.mDictionaryFacilitator);
}
mInputLogic.replaceSuggest(newSuggest);
refreshPersonalizationDictionarySession();
/**
* Reset suggest by loading the main dictionary of the current locale.
*/
/* package private */ void resetSuggestMainDict() {
final DictionaryFacilitatorForSuggest dictionaryFacilitator =
mInputLogic.mSuggest.mDictionaryFacilitator;
final SettingsValues settingsValues = mSettings.getCurrent();
dictionaryFacilitator.resetDictionaries(this /* context */,
dictionaryFacilitator.getLocale(), settingsValues.mUseContactsDict,
settingsValues.mUsePersonalizedDicts, true /* forceReloadMainDictionary */, this);
}
@Override
public void onDestroy() {
final Suggest suggest = mInputLogic.mSuggest;
if (suggest != null) {
suggest.close();
mInputLogic.mSuggest = null;
}
mInputLogic.mSuggest.mDictionaryFacilitator.closeDictionaries();
mSettings.onDestroy();
unregisterReceiver(mReceiver);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
@ -802,10 +779,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Note: the following does a round-trip IPC on the main thread: be careful
final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
Suggest suggest = mInputLogic.mSuggest;
if (null != suggest && null != currentLocale && !currentLocale.equals(suggest.mLocale)) {
initSuggest();
suggest = mInputLogic.mSuggest;
final Suggest suggest = mInputLogic.mSuggest;
if (null != currentLocale && !currentLocale.equals(suggest.getLocale())) {
// TODO: Do this automatically.
resetSuggest();
}
// TODO[IL]: Can the following be moved to InputLogic#startInput?
@ -833,13 +810,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (isDifferentTextField ||
!currentSettingsValues.hasSameOrientation(getResources().getConfiguration())) {
loadSettings();
suggest = mInputLogic.mSuggest;
}
if (isDifferentTextField) {
mainKeyboardView.closing();
currentSettingsValues = mSettings.getCurrent();
if (suggest != null && currentSettingsValues.mCorrectionEnabled) {
if (currentSettingsValues.mCorrectionEnabled) {
suggest.setAutoCorrectionThreshold(currentSettingsValues.mAutoCorrectionThreshold);
}
@ -865,8 +841,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mHandler.cancelUpdateSuggestionStrip();
mainKeyboardView.setMainDictionaryAvailability(null != suggest
? suggest.mDictionaryFacilitator.hasMainDictionary() : false);
mainKeyboardView.setMainDictionaryAvailability(
suggest.mDictionaryFacilitator.hasInitializedMainDictionary());
mainKeyboardView.setKeyPreviewPopupEnabled(currentSettingsValues.mKeyPreviewPopupOn,
currentSettingsValues.mKeyPreviewPopupDismissDelay);
mainKeyboardView.setSlidingKeyInputPreviewEnabled(
@ -1407,8 +1383,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void getSuggestedWords(final int sessionId, final int sequenceNumber,
final OnGetSuggestedWordsCallback callback) {
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
final Suggest suggest = mInputLogic.mSuggest;
if (keyboard == null || suggest == null) {
if (keyboard == null) {
callback.onGetSuggestedWords(SuggestedWords.EMPTY);
return;
}
@ -1437,7 +1412,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
}
suggest.getSuggestedWords(mInputLogic.mWordComposer,
mInputLogic.mSuggest.getSuggestedWords(mInputLogic.mWordComposer,
mInputLogic.mWordComposer.getPreviousWordForSuggestion(),
keyboard.getProximityInfo(), currentSettings.mBlockPotentiallyOffensive,
currentSettings.mCorrectionEnabled, additionalFeaturesOptions, sessionId,
@ -1722,12 +1697,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// DO NOT USE THIS for any other purpose than testing. This can break the keyboard badly.
@UsedForTesting
/* package for test */ void replaceDictionariesForTest(final Locale locale) {
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator =
mInputLogic.mSuggest.mDictionaryFacilitator;
final DictionaryFacilitatorForSuggest dictionaryFacilitator =
new DictionaryFacilitatorForSuggest(this, locale, mSettings.getCurrent(),
this /* listener */, oldDictionaryFacilitator);
replaceSuggest(new Suggest(locale, dictionaryFacilitator));
final SettingsValues settingsValues = mSettings.getCurrent();
mInputLogic.mSuggest.mDictionaryFacilitator.resetDictionaries(this, locale,
settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts,
false /* forceReloadMainDictionary */, this /* listener */);
}
// DO NOT USE THIS for any other purpose than testing.
@ -1738,8 +1711,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
public void dumpDictionaryForDebug(final String dictName) {
if (mInputLogic.mSuggest == null) {
initSuggest();
final DictionaryFacilitatorForSuggest dictionaryFacilitator =
mInputLogic.mSuggest.mDictionaryFacilitator;
if (dictionaryFacilitator.getLocale() == null) {
resetSuggest();
}
mInputLogic.mSuggest.mDictionaryFacilitator.dumpDictionaryForDebug(dictName);
}

View File

@ -23,12 +23,11 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.utils.AutoCorrectionUtils;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
import com.android.inputmethod.latin.utils.BoundedTreeSet;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.StringUtils;
import com.android.inputmethod.latin.utils.SuggestionResults;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Locale;
/**
@ -53,29 +52,16 @@ public final class Suggest {
private static final int SUPPRESS_SUGGEST_THRESHOLD = -2000000000;
private static final boolean DBG = LatinImeLogger.sDBG;
public final DictionaryFacilitatorForSuggest mDictionaryFacilitator;
public final DictionaryFacilitatorForSuggest mDictionaryFacilitator =
new DictionaryFacilitatorForSuggest();
private float mAutoCorrectionThreshold;
// Locale used for upper- and title-casing words
public final Locale mLocale;
// TODO: Move dictionaryFacilitator constructing logics from LatinIME to Suggest.
public Suggest(final Locale locale,
final DictionaryFacilitatorForSuggest dictionaryFacilitator) {
mLocale = locale;
mDictionaryFacilitator = dictionaryFacilitator;
public Locale getLocale() {
return mDictionaryFacilitator.getLocale();
}
// Creates instance with new dictionary facilitator.
public Suggest(final Suggest oldSuggst,
final DictionaryFacilitatorForSuggest dictionaryFacilitator) {
mLocale = oldSuggst.mLocale;
mAutoCorrectionThreshold = oldSuggst.mAutoCorrectionThreshold;
mDictionaryFacilitator = dictionaryFacilitator;
}
public void setAutoCorrectionThreshold(float threshold) {
public void setAutoCorrectionThreshold(final float threshold) {
mAutoCorrectionThreshold = threshold;
}
@ -108,9 +94,6 @@ public final class Suggest {
final int[] additionalFeaturesOptions, final int sequenceNumber,
final OnGetSuggestedWordsCallback callback) {
final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
SuggestedWords.MAX_SUGGESTIONS);
final String typedWord = wordComposer.getTypedWord();
final String consideredWord = trailingSingleQuotesCount > 0
? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount)
@ -132,20 +115,20 @@ public final class Suggest {
} else {
rawSuggestions = null;
}
mDictionaryFacilitator.getSuggestions(wordComposerForLookup, prevWordForBigram,
proximityInfo, blockOffensiveWords, additionalFeaturesOptions, SESSION_TYPING,
suggestionsSet, rawSuggestions);
final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults(
wordComposerForLookup, prevWordForBigram, proximityInfo, blockOffensiveWords,
additionalFeaturesOptions, SESSION_TYPING, rawSuggestions);
final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
final boolean isAllUpperCase = wordComposer.isAllUpperCase();
final String firstSuggestion;
final String whitelistedWord;
if (suggestionsSet.isEmpty()) {
if (suggestionResults.isEmpty()) {
whitelistedWord = firstSuggestion = null;
} else {
final SuggestedWordInfo firstSuggestedWordInfo = getTransformedSuggestedWordInfo(
suggestionsSet.first(), mLocale, isAllUpperCase, isFirstCharCapitalized,
trailingSingleQuotesCount);
suggestionResults.first(), suggestionResults.mLocale, isAllUpperCase,
isFirstCharCapitalized, trailingSingleQuotesCount);
firstSuggestion = firstSuggestedWordInfo.mWord;
if (SuggestedWordInfo.KIND_WHITELIST != firstSuggestedWordInfo.mKind) {
whitelistedWord = null;
@ -175,10 +158,10 @@ public final class Suggest {
// the current settings. It may also be useful to know, when the setting is off, whether
// the word *would* have been auto-corrected.
if (!isCorrectionEnabled || !allowsToBeAutoCorrected || isPrediction
|| suggestionsSet.isEmpty() || wordComposer.hasDigits()
|| suggestionResults.isEmpty() || wordComposer.hasDigits()
|| wordComposer.isMostlyCaps() || wordComposer.isResumed()
|| !mDictionaryFacilitator.hasMainDictionary()
|| SuggestedWordInfo.KIND_SHORTCUT == suggestionsSet.first().mKind) {
|| !mDictionaryFacilitator.hasInitializedMainDictionary()
|| SuggestedWordInfo.KIND_SHORTCUT == suggestionResults.first().mKind) {
// If we don't have a main dictionary, we never want to auto-correct. The reason for
// this is, the user may have a contact whose name happens to match a valid word in
// their language, and it will unexpectedly auto-correct. For example, if the user
@ -190,17 +173,17 @@ public final class Suggest {
hasAutoCorrection = false;
} else {
hasAutoCorrection = AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold(
suggestionsSet.first(), consideredWord, mAutoCorrectionThreshold);
suggestionResults.first(), consideredWord, mAutoCorrectionThreshold);
}
final ArrayList<SuggestedWordInfo> suggestionsContainer =
CollectionUtils.newArrayList(suggestionsSet);
CollectionUtils.newArrayList(suggestionResults);
final int suggestionsCount = suggestionsContainer.size();
if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) {
for (int i = 0; i < suggestionsCount; ++i) {
final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized,
wordInfo, suggestionResults.mLocale, isAllUpperCase, isFirstCharCapitalized,
trailingSingleQuotesCount);
suggestionsContainer.set(i, transformedWordInfo);
}
@ -244,23 +227,21 @@ public final class Suggest {
final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
final int sessionId, final int sequenceNumber,
final OnGetSuggestedWordsCallback callback) {
final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
SuggestedWords.MAX_SUGGESTIONS);
final ArrayList<SuggestedWordInfo> rawSuggestions;
if (ProductionFlag.INCLUDE_RAW_SUGGESTIONS) {
rawSuggestions = CollectionUtils.newArrayList();
} else {
rawSuggestions = null;
}
mDictionaryFacilitator.getSuggestions(wordComposer, prevWordForBigram, proximityInfo,
blockOffensiveWords, additionalFeaturesOptions, sessionId, suggestionsSet,
rawSuggestions);
for (SuggestedWordInfo wordInfo : suggestionsSet) {
final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults(
wordComposer, prevWordForBigram, proximityInfo, blockOffensiveWords,
additionalFeaturesOptions, sessionId, rawSuggestions);
for (SuggestedWordInfo wordInfo : suggestionResults) {
LatinImeLogger.onAddSuggestedWord(wordInfo.mWord, wordInfo.mSourceDict.mDictType);
}
final ArrayList<SuggestedWordInfo> suggestionsContainer =
CollectionUtils.newArrayList(suggestionsSet);
CollectionUtils.newArrayList(suggestionResults);
final int suggestionsCount = suggestionsContainer.size();
final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock();
final boolean isAllUpperCase = wordComposer.isAllUpperCase();
@ -268,7 +249,7 @@ public final class Suggest {
for (int i = 0; i < suggestionsCount; ++i) {
final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized,
wordInfo, suggestionResults.mLocale, isAllUpperCase, isFirstCharCapitalized,
0 /* trailingSingleQuotesCount */);
suggestionsContainer.set(i, transformedWordInfo);
}
@ -326,22 +307,6 @@ public final class Suggest {
return suggestionsList;
}
private static final class SuggestedWordInfoComparator
implements Comparator<SuggestedWordInfo> {
// This comparator ranks the word info with the higher frequency first. That's because
// that's the order we want our elements in.
@Override
public int compare(final SuggestedWordInfo o1, final SuggestedWordInfo o2) {
if (o1.mScore > o2.mScore) return -1;
if (o1.mScore < o2.mScore) return 1;
if (o1.mCodePointCount < o2.mCodePointCount) return -1;
if (o1.mCodePointCount > o2.mCodePointCount) return 1;
return o1.mWord.compareTo(o2.mWord);
}
}
private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator =
new SuggestedWordInfoComparator();
/* package for test */ static SuggestedWordInfo getTransformedSuggestedWordInfo(
final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase,
final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) {
@ -365,8 +330,4 @@ public final class Suggest {
wordInfo.mSourceDict, wordInfo.mIndexOfTouchPointOfSecondWord,
wordInfo.mAutoCommitFirstWordConfidence);
}
public void close() {
mDictionaryFacilitator.close();
}
}

View File

@ -32,6 +32,7 @@ import com.android.inputmethod.event.InputTransaction;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.DictionaryFacilitatorForSuggest;
import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.LastComposedWord;
import com.android.inputmethod.latin.LatinIME;
@ -77,8 +78,7 @@ public final class InputLogic {
private int mSpaceState;
// Never null
public SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
// TODO: mSuggest should be touched by a single thread.
public volatile Suggest mSuggest;
public final Suggest mSuggest = new Suggest();
public LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
public final WordComposer mWordComposer;
@ -106,15 +106,6 @@ public final class InputLogic {
mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
}
// Replace the old Suggest with the passed Suggest and close it.
public void replaceSuggest(final Suggest newSuggest) {
final Suggest oldSuggest = mSuggest;
mSuggest = newSuggest;
if (oldSuggest != null) {
oldSuggest.close();
}
}
/**
* Initializes the input logic for input in an editor.
*
@ -283,23 +274,18 @@ public final class InputLogic {
// We should show the "Touch again to save" hint if the user pressed the first entry
// AND it's in none of our current dictionaries (main, user or otherwise).
// Please note that if mSuggest is null, it means that everything is off: suggestion
// and correction, so we shouldn't try to show the hint
final Suggest suggest = mSuggest;
final DictionaryFacilitatorForSuggest dictionaryFacilitator =
mSuggest.mDictionaryFacilitator;
final boolean showingAddToDictionaryHint =
(SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind
|| SuggestedWordInfo.KIND_OOV_CORRECTION == suggestionInfo.mKind)
&& suggest != null
// If the suggestion is not in the dictionary, the hint should be shown.
&& !suggest.mDictionaryFacilitator.isValidWord(suggestion,
true /* ignoreCase */);
&& !dictionaryFacilitator.isValidWord(suggestion, true /* ignoreCase */);
if (settingsValues.mIsInternal) {
LatinImeLoggerUtils.onSeparator((char)Constants.CODE_SPACE,
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
}
if (showingAddToDictionaryHint
&& suggest.mDictionaryFacilitator.isUserDictionaryEnabled()) {
if (showingAddToDictionaryHint && dictionaryFacilitator.isUserDictionaryEnabled()) {
mSuggestionStripViewAccessor.showAddToDictionaryHint(suggestion);
} else {
// If we're not showing the "Touch again to save", then update the suggestion strip.
@ -1231,20 +1217,17 @@ public final class InputLogic {
if (!settingsValues.mCorrectionEnabled) return;
if (TextUtils.isEmpty(suggestion)) return;
final Suggest suggest = mSuggest;
if (suggest == null) return;
final boolean wasAutoCapitalized =
mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps();
final int timeStampInSeconds = (int)TimeUnit.MILLISECONDS.toSeconds(
System.currentTimeMillis());
suggest.mDictionaryFacilitator.addToUserHistory(suggestion, wasAutoCapitalized, prevWord,
mSuggest.mDictionaryFacilitator.addToUserHistory(suggestion, wasAutoCapitalized, prevWord,
timeStampInSeconds);
}
public void performUpdateSuggestionStripSync(final SettingsValues settingsValues) {
// Check if we have a suggestion engine attached.
if (mSuggest == null || !settingsValues.isSuggestionsRequested()) {
if (!settingsValues.isSuggestionsRequested()) {
if (mWordComposer.isComposingWord()) {
Log.w(TAG, "Called updateSuggestionsOrPredictions but suggestions were not "
+ "requested!");
@ -1446,10 +1429,8 @@ public final class InputLogic {
}
mConnection.deleteSurroundingText(deleteLength, 0);
if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) {
if (mSuggest != null) {
mSuggest.mDictionaryFacilitator.cancelAddingUserHistory(
previousWord, committedWordString);
}
mSuggest.mDictionaryFacilitator.cancelAddingUserHistory(
previousWord, committedWordString);
}
final String stringToCommit = originallyTypedWord + mLastComposedWord.mSeparatorString;
final SpannableString textToCommit = new SpannableString(stringToCommit);

View File

@ -1,49 +0,0 @@
/*
* Copyright (C) 2012 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.utils;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import java.util.Collection;
import java.util.Comparator;
import java.util.TreeSet;
/**
* A TreeSet that is bounded in size and throws everything that's smaller than its limit
*/
public final class BoundedTreeSet extends TreeSet<SuggestedWordInfo> {
private final int mCapacity;
public BoundedTreeSet(final Comparator<SuggestedWordInfo> comparator, final int capacity) {
super(comparator);
mCapacity = capacity;
}
@Override
public boolean add(final SuggestedWordInfo e) {
if (size() < mCapacity) return super.add(e);
if (comparator().compare(e, last()) > 0) return false;
super.add(e);
pollLast(); // removes the last element
return true;
}
@Override
public boolean addAll(final Collection<? extends SuggestedWordInfo> e) {
if (null == e) return false;
return super.addAll(e);
}
}

View File

@ -119,7 +119,7 @@ public final class LanguageModelParam {
private static LanguageModelParam detectWhetherVaildWordOrNotAndGetLanguageModelParam(
final String prevWord, final String targetWord, final int timestamp,
final DictionaryFacilitatorForSuggest dictionaryFacilitator) {
final Locale locale = dictionaryFacilitator.mLocale;
final Locale locale = dictionaryFacilitator.getLocale();
if (!dictionaryFacilitator.isValidWord(targetWord, true /* ignoreCase */)) {
// OOV word.
return createAndGetLanguageModelParamOfWord(prevWord, targetWord, timestamp,

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2014 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.utils;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import java.util.Collection;
import java.util.Comparator;
import java.util.Locale;
import java.util.TreeSet;
/**
* A TreeSet of SuggestedWordInfo that is bounded in size and throws everything that's smaller
* than its limit
*/
public final class SuggestionResults extends TreeSet<SuggestedWordInfo> {
public final Locale mLocale;
private final int mCapacity;
public SuggestionResults(final Locale locale, final int capacity) {
this(locale, sSuggestedWordInfoComparator, capacity);
}
public SuggestionResults(final Locale locale, final Comparator<SuggestedWordInfo> comparator,
final int capacity) {
super(comparator);
mLocale = locale;
mCapacity = capacity;
}
@Override
public boolean add(final SuggestedWordInfo e) {
if (size() < mCapacity) return super.add(e);
if (comparator().compare(e, last()) > 0) return false;
super.add(e);
pollLast(); // removes the last element
return true;
}
@Override
public boolean addAll(final Collection<? extends SuggestedWordInfo> e) {
if (null == e) return false;
return super.addAll(e);
}
private static final class SuggestedWordInfoComparator
implements Comparator<SuggestedWordInfo> {
// This comparator ranks the word info with the higher frequency first. That's because
// that's the order we want our elements in.
@Override
public int compare(final SuggestedWordInfo o1, final SuggestedWordInfo o2) {
if (o1.mScore > o2.mScore) return -1;
if (o1.mScore < o2.mScore) return 1;
if (o1.mCodePointCount < o2.mCodePointCount) return -1;
if (o1.mCodePointCount > o2.mCodePointCount) return 1;
return o1.mWord.compareTo(o2.mWord);
}
}
private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator =
new SuggestedWordInfoComparator();
}

View File

@ -155,8 +155,9 @@ public abstract class MainLogBuffer extends FixedLogBuffer {
}
// Reload the dictionary in case it has changed (e.g., because the user has changed
// languages).
if ((mDictionaryFacilitator == null || !mDictionaryFacilitator.hasMainDictionary())
&& mDictionaryForTesting == null) {
if ((mDictionaryFacilitator == null
|| !mDictionaryFacilitator.hasInitializedMainDictionary())
&& mDictionaryForTesting == null) {
// Main dictionary is unavailable. Since we cannot check it, we cannot tell if a
// word is out-of-vocabulary or not. Therefore, we must judge the entire buffer
// contents to potentially pose a privacy risk.