am eea7122e: Move dict operations to Suggest.

* commit 'eea7122e5b0b0489e4795e3e6cb994784b4ab82b':
  Move dict operations to Suggest.
main
Keisuke Kuroyanagi 2013-12-24 22:47:10 -08:00 committed by Android Git Automerger
commit 4dd23b9dce
4 changed files with 96 additions and 98 deletions

View File

@ -128,8 +128,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private final SubtypeSwitcher mSubtypeSwitcher; private final SubtypeSwitcher mSubtypeSwitcher;
private final SubtypeState mSubtypeState = new SubtypeState(); private final SubtypeState mSubtypeState = new SubtypeState();
private UserBinaryDictionary mUserDictionary;
// Object for reacting to adding/removing a dictionary pack. // Object for reacting to adding/removing a dictionary pack.
private BroadcastReceiver mDictionaryPackInstallReceiver = private BroadcastReceiver mDictionaryPackInstallReceiver =
new DictionaryPackInstallBroadcastReceiver(this); new DictionaryPackInstallBroadcastReceiver(this);
@ -518,6 +516,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final SettingsValues currentSettingsValues = mSettings.getCurrent(); final SettingsValues currentSettingsValues = mSettings.getCurrent();
if (!mHandler.hasPendingReopenDictionaries() && mInputLogic.mSuggest != null) { if (!mHandler.hasPendingReopenDictionaries() && mInputLogic.mSuggest != null) {
// May need to reset dictionaries depending on the user settings. // May need to reset dictionaries depending on the user settings.
// TODO: Quit setting dictionaries from LatinIME.
mInputLogic.mSuggest.setAdditionalDictionaries(mInputLogic.mSuggest /* oldSuggest */, mInputLogic.mSuggest.setAdditionalDictionaries(mInputLogic.mSuggest /* oldSuggest */,
currentSettingsValues); currentSettingsValues);
} }
@ -565,9 +564,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.getInstance().initSuggest(newSuggest); ResearchLogger.getInstance().initSuggest(newSuggest);
} }
// TODO: Quit setting dictionaries from LatinIME.
mUserDictionary = new UserBinaryDictionary(this, subtypeLocale);
newSuggest.setUserDictionary(mUserDictionary);
newSuggest.setAdditionalDictionaries(mInputLogic.mSuggest /* oldSuggest */, newSuggest.setAdditionalDictionaries(mInputLogic.mSuggest /* oldSuggest */,
mSettings.getCurrent()); mSettings.getCurrent());
final Suggest oldSuggest = mInputLogic.mSuggest; final Suggest oldSuggest = mInputLogic.mSuggest;
@ -1208,7 +1205,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else { } else {
wordToEdit = word; wordToEdit = word;
} }
mUserDictionary.addWordToUserDictionary(wordToEdit); mInputLogic.mSuggest.addWordToUserDictionary(wordToEdit);
} }
public void displaySettingsDialog() { public void displaySettingsDialog() {
@ -1739,13 +1736,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|| SuggestedWordInfo.KIND_OOV_CORRECTION == suggestionInfo.mKind) || SuggestedWordInfo.KIND_OOV_CORRECTION == suggestionInfo.mKind)
&& suggest != null && suggest != null
// If the suggestion is not in the dictionary, the hint should be shown. // If the suggestion is not in the dictionary, the hint should be shown.
&& !AutoCorrectionUtils.isValidWord(suggest, suggestion, true); && !suggest.isValidWord(suggestion, true);
if (currentSettings.mIsInternal) { if (currentSettings.mIsInternal) {
LatinImeLoggerUtils.onSeparator((char)Constants.CODE_SPACE, LatinImeLoggerUtils.onSeparator((char)Constants.CODE_SPACE,
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
} }
if (showingAddToDictionaryHint && mUserDictionary.mEnabled) { if (showingAddToDictionaryHint && suggest.isUserDictionaryEnabled()) {
mSuggestionStripView.showAddToDictionaryHint( mSuggestionStripView.showAddToDictionaryHint(
suggestion, currentSettings.mHintToSaveText); suggestion, currentSettings.mHintToSaveText);
} else { } else {

View File

@ -39,11 +39,13 @@ import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/** /**
* This class loads a dictionary and provides a list of suggestions for a given sequence of * This class loads a dictionary and provides a list of suggestions for a given sequence of
* characters. This includes corrections and completions. * characters. This includes corrections and completions.
*/ */
// TODO: Separate dictionary operations from suggestions handling logic.
public final class Suggest { public final class Suggest {
public static final String TAG = Suggest.class.getSimpleName(); public static final String TAG = Suggest.class.getSimpleName();
@ -74,6 +76,7 @@ public final class Suggest {
private HashSet<String> mOnlyDictionarySetForDebug = null; private HashSet<String> mOnlyDictionarySetForDebug = null;
private Dictionary mMainDictionary; private Dictionary mMainDictionary;
private ContactsBinaryDictionary mContactsDictionary; private ContactsBinaryDictionary mContactsDictionary;
private UserBinaryDictionary mUserDictionary;
private UserHistoryDictionary mUserHistoryDictionary; private UserHistoryDictionary mUserHistoryDictionary;
private PersonalizationDictionary mPersonalizationDictionary; private PersonalizationDictionary mPersonalizationDictionary;
@UsedForTesting @UsedForTesting
@ -98,6 +101,7 @@ public final class Suggest {
mOnlyDictionarySetForDebug = new HashSet<String>(); mOnlyDictionarySetForDebug = new HashSet<String>();
mOnlyDictionarySetForDebug.add(Dictionary.TYPE_PERSONALIZATION); mOnlyDictionarySetForDebug.add(Dictionary.TYPE_PERSONALIZATION);
} }
setUserDictionary(new UserBinaryDictionary(context, locale));
} }
@UsedForTesting @UsedForTesting
@ -171,27 +175,13 @@ public final class Suggest {
return mMainDictionary; return mMainDictionary;
} }
public ContactsBinaryDictionary getContactsDictionary() {
return mContactsDictionary;
}
public UserHistoryDictionary getUserHistoryDictionary() {
return mUserHistoryDictionary;
}
public PersonalizationDictionary getPersonalizationDictionary() {
return mPersonalizationDictionary;
}
public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() {
return mDictionaries;
}
/** /**
* Sets an optional user dictionary resource to be loaded. The user dictionary is consulted * 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. * before the main dictionary, if set. This refers to the system-managed user dictionary.
*/ */
@UsedForTesting
public void setUserDictionary(final UserBinaryDictionary userDictionary) { public void setUserDictionary(final UserBinaryDictionary userDictionary) {
mUserDictionary = userDictionary;
addOrReplaceDictionaryInternal(Dictionary.TYPE_USER, userDictionary); addOrReplaceDictionaryInternal(Dictionary.TYPE_USER, userDictionary);
} }
@ -200,17 +190,18 @@ public final class Suggest {
* the contacts dictionary by passing null to this method. In this case no contacts dictionary * the contacts dictionary by passing null to this method. In this case no contacts dictionary
* won't be used. * won't be used.
*/ */
@UsedForTesting
public void setContactsDictionary(final ContactsBinaryDictionary contactsDictionary) { public void setContactsDictionary(final ContactsBinaryDictionary contactsDictionary) {
mContactsDictionary = contactsDictionary; mContactsDictionary = contactsDictionary;
addOrReplaceDictionaryInternal(Dictionary.TYPE_CONTACTS, contactsDictionary); addOrReplaceDictionaryInternal(Dictionary.TYPE_CONTACTS, contactsDictionary);
} }
public void setUserHistoryDictionary(final UserHistoryDictionary userHistoryDictionary) { private void setUserHistoryDictionary(final UserHistoryDictionary userHistoryDictionary) {
mUserHistoryDictionary = userHistoryDictionary; mUserHistoryDictionary = userHistoryDictionary;
addOrReplaceDictionaryInternal(Dictionary.TYPE_USER_HISTORY, userHistoryDictionary); addOrReplaceDictionaryInternal(Dictionary.TYPE_USER_HISTORY, userHistoryDictionary);
} }
public void setPersonalizationDictionary( private void setPersonalizationDictionary(
final PersonalizationDictionary personalizationDictionary) { final PersonalizationDictionary personalizationDictionary) {
mPersonalizationDictionary = personalizationDictionary; mPersonalizationDictionary = personalizationDictionary;
addOrReplaceDictionaryInternal(Dictionary.TYPE_PERSONALIZATION, personalizationDictionary); addOrReplaceDictionaryInternal(Dictionary.TYPE_PERSONALIZATION, personalizationDictionary);
@ -225,7 +216,7 @@ public final class Suggest {
public void setAdditionalDictionaries(final Suggest oldSuggest, public void setAdditionalDictionaries(final Suggest oldSuggest,
final SettingsValues settingsValues) { final SettingsValues settingsValues) {
// Contacts dictionary // Contacts dictionary
resetContactsDictionary(null != oldSuggest ? oldSuggest.getContactsDictionary() : null, resetContactsDictionary(null != oldSuggest ? oldSuggest.mContactsDictionary : null,
settingsValues); settingsValues);
// User history dictionary & Personalization dictionary // User history dictionary & Personalization dictionary
resetPersonalizedDictionaries(oldSuggest, settingsValues); resetPersonalizedDictionaries(oldSuggest, settingsValues);
@ -245,9 +236,9 @@ public final class Suggest {
final boolean shouldSetDictionaries = settingsValues.mUsePersonalizedDicts; final boolean shouldSetDictionaries = settingsValues.mUsePersonalizedDicts;
final UserHistoryDictionary oldUserHistoryDictionary = (null == oldSuggest) ? null : final UserHistoryDictionary oldUserHistoryDictionary = (null == oldSuggest) ? null :
oldSuggest.getUserHistoryDictionary(); oldSuggest.mUserHistoryDictionary;
final PersonalizationDictionary oldPersonalizationDictionary = (null == oldSuggest) ? null : final PersonalizationDictionary oldPersonalizationDictionary = (null == oldSuggest) ? null :
oldSuggest.getPersonalizationDictionary(); oldSuggest.mPersonalizationDictionary;
final UserHistoryDictionary userHistoryDictionaryToUse; final UserHistoryDictionary userHistoryDictionaryToUse;
final PersonalizationDictionary personalizationDictionaryToUse; final PersonalizationDictionary personalizationDictionaryToUse;
if (!shouldSetDictionaries) { if (!shouldSetDictionaries) {
@ -311,6 +302,43 @@ public final class Suggest {
setContactsDictionary(dictionaryToUse); setContactsDictionary(dictionaryToUse);
} }
public boolean isUserDictionaryEnabled() {
if (mUserDictionary == null) {
return false;
}
return mUserDictionary.mEnabled;
}
public void addWordToUserDictionary(String word) {
if (mUserDictionary == null) {
return;
}
mUserDictionary.addWordToUserDictionary(word);
}
public String addToUserHistory(final WordComposer wordComposer, final String previousWord,
final String suggestion) {
if (mUserHistoryDictionary == null) {
return null;
}
final String secondWord;
if (wordComposer.wasAutoCapitalized() && !wordComposer.isMostlyCaps()) {
secondWord = suggestion.toLowerCase(mLocale);
} else {
secondWord = suggestion;
}
// 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 int maxFreq = getMaxFrequency(suggestion);
if (maxFreq == 0) {
return null;
}
final boolean isValid = maxFreq > 0;
final int timeStamp = (int)TimeUnit.MILLISECONDS.toSeconds((System.currentTimeMillis()));
mUserHistoryDictionary.addToDictionary(previousWord, secondWord, isValid, timeStamp);
return previousWord;
}
public void cancelAddingUserHistory(final String previousWord, final String committedWord) { public void cancelAddingUserHistory(final String previousWord, final String committedWord) {
if (mUserHistoryDictionary != null) { if (mUserHistoryDictionary != null) {
mUserHistoryDictionary.cancelAddingUserHistory(previousWord, committedWord); mUserHistoryDictionary.cancelAddingUserHistory(previousWord, committedWord);
@ -389,8 +417,8 @@ public final class Suggest {
// or if it's a 2+ characters non-word (i.e. it's not in the dictionary). // or if it's a 2+ characters non-word (i.e. it's not in the dictionary).
final boolean allowsToBeAutoCorrected = (null != whitelistedWord final boolean allowsToBeAutoCorrected = (null != whitelistedWord
&& !whitelistedWord.equals(consideredWord)) && !whitelistedWord.equals(consideredWord))
|| (consideredWord.length() > 1 && !AutoCorrectionUtils.isValidWord(this, || (consideredWord.length() > 1
consideredWord, wordComposer.isFirstCharCapitalized())); && !isValidWord(consideredWord, wordComposer.isFirstCharCapitalized()));
final boolean hasAutoCorrection; final boolean hasAutoCorrection;
// TODO: using isCorrectionEnabled here is not very good. It's probably useless, because // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because
@ -594,6 +622,45 @@ public final class Suggest {
wordInfo.mAutoCommitFirstWordConfidence); wordInfo.mAutoCommitFirstWordConfidence);
} }
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?
// Ideally the passed map would come out of a {@link java.util.concurrent.Future} and
// would be immutable once it's finished initializing, but concretely a null test is
// probably good enough for the time being.
if (null == dictionary) continue;
if (dictionary.isValidWord(word)
|| (ignoreCase && dictionary.isValidWord(lowerCasedWord))) {
return true;
}
}
return false;
}
private int getMaxFrequency(final String word) {
if (TextUtils.isEmpty(word)) {
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 int tempFreq = dictionary.getFrequency(word);
if (tempFreq >= maxFreq) {
maxFreq = tempFreq;
}
}
return maxFreq;
}
public void close() { public void close() {
final HashSet<Dictionary> dictionaries = CollectionUtils.newHashSet(); final HashSet<Dictionary> dictionaries = CollectionUtils.newHashSet();
dictionaries.addAll(mDictionaries.values()); dictionaries.addAll(mDictionaries.values());

View File

@ -44,12 +44,10 @@ import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.WordComposer;
import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.personalization.UserHistoryDictionary;
import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.suggestions.SuggestionStripView; import com.android.inputmethod.latin.suggestions.SuggestionStripView;
import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.AsyncResultHolder;
import com.android.inputmethod.latin.utils.AutoCorrectionUtils;
import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.InputTypeUtils; import com.android.inputmethod.latin.utils.InputTypeUtils;
import com.android.inputmethod.latin.utils.LatinImeLoggerUtils; import com.android.inputmethod.latin.utils.LatinImeLoggerUtils;
@ -60,7 +58,6 @@ import com.android.inputmethod.research.ResearchLogger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
/** /**
* This class manages the input logic. * This class manages the input logic.
@ -977,24 +974,8 @@ public final class InputLogic {
final Suggest suggest = mSuggest; final Suggest suggest = mSuggest;
if (suggest == null) return null; if (suggest == null) return null;
final UserHistoryDictionary userHistoryDictionary = suggest.getUserHistoryDictionary();
if (userHistoryDictionary == null) return null;
final String prevWord = mConnection.getNthPreviousWord(settingsValues, 2); final String prevWord = mConnection.getNthPreviousWord(settingsValues, 2);
final String secondWord; return suggest.addToUserHistory(mWordComposer, prevWord, suggestion);
if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
secondWord = suggestion.toLowerCase(settingsValues.mLocale);
} else {
secondWord = suggestion;
}
// 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 int maxFreq = AutoCorrectionUtils.getMaxFrequency(
suggest.getUnigramDictionaries(), suggestion);
if (maxFreq == 0) return null;
userHistoryDictionary.addToDictionary(prevWord, secondWord, maxFreq > 0,
(int)TimeUnit.MILLISECONDS.toSeconds((System.currentTimeMillis())));
return prevWord;
} }
public void performUpdateSuggestionStripSync(final SettingsValues settingsValues, public void performUpdateSuggestionStripSync(final SettingsValues settingsValues,

View File

@ -17,16 +17,11 @@
package com.android.inputmethod.latin.utils; package com.android.inputmethod.latin.utils;
import com.android.inputmethod.latin.BinaryDictionary; import com.android.inputmethod.latin.BinaryDictionary;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.Suggest;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import java.util.concurrent.ConcurrentHashMap;
public final class AutoCorrectionUtils { public final class AutoCorrectionUtils {
private static final boolean DBG = LatinImeLogger.sDBG; private static final boolean DBG = LatinImeLogger.sDBG;
private static final String TAG = AutoCorrectionUtils.class.getSimpleName(); private static final String TAG = AutoCorrectionUtils.class.getSimpleName();
@ -36,48 +31,6 @@ public final class AutoCorrectionUtils {
// Purely static class: can't instantiate. // Purely static class: can't instantiate.
} }
public static boolean isValidWord(final Suggest suggest, final String word,
final boolean ignoreCase) {
if (TextUtils.isEmpty(word)) {
return false;
}
final ConcurrentHashMap<String, Dictionary> dictionaries = suggest.getUnigramDictionaries();
final String lowerCasedWord = word.toLowerCase(suggest.mLocale);
for (final String key : dictionaries.keySet()) {
final Dictionary dictionary = dictionaries.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?
// Ideally the passed map would come out of a {@link java.util.concurrent.Future} and
// would be immutable once it's finished initializing, but concretely a null test is
// probably good enough for the time being.
if (null == dictionary) continue;
if (dictionary.isValidWord(word)
|| (ignoreCase && dictionary.isValidWord(lowerCasedWord))) {
return true;
}
}
return false;
}
public static int getMaxFrequency(final ConcurrentHashMap<String, Dictionary> dictionaries,
final String word) {
if (TextUtils.isEmpty(word)) {
return Dictionary.NOT_A_PROBABILITY;
}
int maxFreq = -1;
for (final String key : dictionaries.keySet()) {
final Dictionary dictionary = dictionaries.get(key);
if (null == dictionary) continue;
final int tempFreq = dictionary.getFrequency(word);
if (tempFreq >= maxFreq) {
maxFreq = tempFreq;
}
}
return maxFreq;
}
public static boolean suggestionExceedsAutoCorrectionThreshold( public static boolean suggestionExceedsAutoCorrectionThreshold(
final SuggestedWordInfo suggestion, final String consideredWord, final SuggestedWordInfo suggestion, final String consideredWord,
final float autoCorrectionThreshold) { final float autoCorrectionThreshold) {