Merge "Support multiple locales in distractor filter."

main
Keisuke Kuroyanagi 2014-09-01 03:27:34 +00:00 committed by Android (Google) Code Review
commit dec2c2d910
1 changed files with 66 additions and 63 deletions

View File

@ -20,12 +20,14 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.text.InputType; import android.text.InputType;
import android.util.Log; import android.util.Log;
import android.util.LruCache; import android.util.LruCache;
import android.util.Pair;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype; import android.view.inputmethod.InputMethodSubtype;
@ -49,16 +51,15 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr
DistracterFilterCheckingExactMatchesAndSuggestions.class.getSimpleName(); DistracterFilterCheckingExactMatchesAndSuggestions.class.getSimpleName();
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
private static final int MAX_DISTRACTERS_CACHE_SIZE = 512; private static final int MAX_DISTRACTERS_CACHE_SIZE = 1024;
private final Context mContext; private final Context mContext;
private final Map<Locale, InputMethodSubtype> mLocaleToSubtypeMap; private final ConcurrentHashMap<Locale, InputMethodSubtype> mLocaleToSubtypeCache;
private final Map<Locale, Keyboard> mLocaleToKeyboardMap; private final ConcurrentHashMap<Locale, Keyboard> mLocaleToKeyboardCache;
private final DictionaryFacilitatorLruCache mDictionaryFacilitatorLruCache; private final DictionaryFacilitatorLruCache mDictionaryFacilitatorLruCache;
private final LruCache<String, Boolean> mDistractersCache; // The key is a pair of a locale and a word. The value indicates the word is a distracter to
// TODO: Remove and support multiple locales at the same time. // words of the locale.
private Locale mCurrentLocale; private final LruCache<Pair<Locale, String>, Boolean> mDistractersCache;
private Keyboard mKeyboard;
private final Object mLock = new Object(); private final Object mLock = new Object();
// If the score of the top suggestion exceeds this value, the tested word (e.g., // If the score of the top suggestion exceeds this value, the tested word (e.g.,
@ -78,20 +79,17 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr
public DistracterFilterCheckingExactMatchesAndSuggestions(final Context context, public DistracterFilterCheckingExactMatchesAndSuggestions(final Context context,
final DictionaryFacilitatorLruCache dictionaryFacilitatorLruCache) { final DictionaryFacilitatorLruCache dictionaryFacilitatorLruCache) {
mContext = context; mContext = context;
mLocaleToSubtypeMap = new HashMap<>(); mLocaleToSubtypeCache = new ConcurrentHashMap<>();
mLocaleToKeyboardMap = new HashMap<>(); mLocaleToKeyboardCache = new ConcurrentHashMap<>();
mDictionaryFacilitatorLruCache = dictionaryFacilitatorLruCache; mDictionaryFacilitatorLruCache = dictionaryFacilitatorLruCache;
mDistractersCache = new LruCache<>(MAX_DISTRACTERS_CACHE_SIZE); mDistractersCache = new LruCache<>(MAX_DISTRACTERS_CACHE_SIZE);
mCurrentLocale = null;
mKeyboard = null;
} }
@Override @Override
public void close() { public void close() {
mLocaleToKeyboardMap.clear(); mLocaleToSubtypeCache.clear();
mLocaleToKeyboardCache.clear();
mDistractersCache.evictAll(); mDistractersCache.evictAll();
mCurrentLocale = null;
mKeyboard = null;
} }
@Override @Override
@ -108,29 +106,36 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr
newLocaleToSubtypeMap.put(locale, subtype); newLocaleToSubtypeMap.put(locale, subtype);
} }
} }
if (mLocaleToSubtypeMap.equals(newLocaleToSubtypeMap)) { if (mLocaleToSubtypeCache.equals(newLocaleToSubtypeMap)) {
// Enabled subtypes have not been changed. // Enabled subtypes have not been changed.
return; return;
} }
synchronized (mLock) { // Update subtype and keyboard map for locales that are in the current mapping.
mLocaleToSubtypeMap.clear(); for (final Locale locale: mLocaleToSubtypeCache.keySet()) {
mLocaleToSubtypeMap.putAll(newLocaleToSubtypeMap); if (newLocaleToSubtypeMap.containsKey(locale)) {
mLocaleToKeyboardMap.clear(); final InputMethodSubtype newSubtype = newLocaleToSubtypeMap.remove(locale);
if (newSubtype.equals(newLocaleToSubtypeMap.get(locale))) {
// Mapping has not been changed.
continue;
}
mLocaleToSubtypeCache.replace(locale, newSubtype);
} else {
mLocaleToSubtypeCache.remove(locale);
}
mLocaleToKeyboardCache.remove(locale);
} }
// Add locales that are not in the current mapping.
mLocaleToSubtypeCache.putAll(newLocaleToSubtypeMap);
} }
private void loadKeyboardForLocale(final Locale newLocale) { private Keyboard getKeyboardForLocale(final Locale locale) {
final Keyboard cachedKeyboard = mLocaleToKeyboardMap.get(newLocale); final Keyboard cachedKeyboard = mLocaleToKeyboardCache.get(locale);
if (cachedKeyboard != null) { if (cachedKeyboard != null) {
mKeyboard = cachedKeyboard; return cachedKeyboard;
return;
}
final InputMethodSubtype subtype;
synchronized (mLock) {
subtype = mLocaleToSubtypeMap.get(newLocale);
} }
final InputMethodSubtype subtype = mLocaleToSubtypeCache.get(locale);
if (subtype == null) { if (subtype == null) {
return; return null;
} }
final EditorInfo editorInfo = new EditorInfo(); final EditorInfo editorInfo = new EditorInfo();
editorInfo.inputType = InputType.TYPE_CLASS_TEXT; editorInfo.inputType = InputType.TYPE_CLASS_TEXT;
@ -143,7 +148,9 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr
builder.setSubtype(new RichInputMethodSubtype(subtype)); builder.setSubtype(new RichInputMethodSubtype(subtype));
builder.setIsSpellChecker(false /* isSpellChecker */); builder.setIsSpellChecker(false /* isSpellChecker */);
final KeyboardLayoutSet layoutSet = builder.build(); final KeyboardLayoutSet layoutSet = builder.build();
mKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); final Keyboard newKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
mLocaleToKeyboardCache.put(locale, newKeyboard);
return newKeyboard;
} }
/** /**
@ -161,24 +168,18 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr
if (locale == null) { if (locale == null) {
return false; return false;
} }
if (!locale.equals(mCurrentLocale)) { if (!mLocaleToSubtypeCache.containsKey(locale)) {
synchronized (mLock) { Log.e(TAG, "Locale " + locale + " is not enabled.");
if (!mLocaleToSubtypeMap.containsKey(locale)) { // TODO: Investigate what we should do for disabled locales.
Log.e(TAG, "Locale " + locale + " is not enabled."); return false;
// TODO: Investigate what we should do for disabled locales.
return false;
}
mCurrentLocale = locale;
loadKeyboardForLocale(locale);
mDistractersCache.evictAll();
}
} }
final DictionaryFacilitator dictionaryFacilitator = final DictionaryFacilitator dictionaryFacilitator =
mDictionaryFacilitatorLruCache.get(locale); mDictionaryFacilitatorLruCache.get(locale);
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "testedWord: " + testedWord); Log.d(TAG, "testedWord: " + testedWord);
} }
final Boolean isCachedDistracter = mDistractersCache.get(testedWord); final Pair<Locale, String> cacheKey = new Pair<>(locale, testedWord);
final Boolean isCachedDistracter = mDistractersCache.get(cacheKey);
if (isCachedDistracter != null && isCachedDistracter) { if (isCachedDistracter != null && isCachedDistracter) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "isDistracter: true (cache hit)"); Log.d(TAG, "isDistracter: true (cache hit)");
@ -189,8 +190,8 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr
final boolean isDistracterCheckedByGetMaxFreqencyOfExactMatches = final boolean isDistracterCheckedByGetMaxFreqencyOfExactMatches =
checkDistracterUsingMaxFreqencyOfExactMatches(dictionaryFacilitator, testedWord); checkDistracterUsingMaxFreqencyOfExactMatches(dictionaryFacilitator, testedWord);
if (isDistracterCheckedByGetMaxFreqencyOfExactMatches) { if (isDistracterCheckedByGetMaxFreqencyOfExactMatches) {
// Add the word to the cache. // Add the pair of locale and word to the cache.
mDistractersCache.put(testedWord, Boolean.TRUE); mDistractersCache.put(cacheKey, Boolean.TRUE);
return true; return true;
} }
final boolean isValidWord = dictionaryFacilitator.isValidWord(testedWord, final boolean isValidWord = dictionaryFacilitator.isValidWord(testedWord,
@ -203,11 +204,12 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr
return false; return false;
} }
final Keyboard keyboard = getKeyboardForLocale(locale);
final boolean isDistracterCheckedByGetSuggestion = final boolean isDistracterCheckedByGetSuggestion =
checkDistracterUsingGetSuggestions(dictionaryFacilitator, testedWord); checkDistracterUsingGetSuggestions(dictionaryFacilitator, keyboard, testedWord);
if (isDistracterCheckedByGetSuggestion) { if (isDistracterCheckedByGetSuggestion) {
// Add the word to the cache. // Add the pair of locale and word to the cache.
mDistractersCache.put(testedWord, Boolean.TRUE); mDistractersCache.put(cacheKey, Boolean.TRUE);
return true; return true;
} }
return false; return false;
@ -229,8 +231,9 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr
} }
private boolean checkDistracterUsingGetSuggestions( private boolean checkDistracterUsingGetSuggestions(
final DictionaryFacilitator dictionaryFacilitator, final String testedWord) { final DictionaryFacilitator dictionaryFacilitator, final Keyboard keyboard,
if (mKeyboard == null) { final String testedWord) {
if (keyboard == null) {
return false; return false;
} }
final SettingsValuesForSuggestion settingsValuesForSuggestion = final SettingsValuesForSuggestion settingsValuesForSuggestion =
@ -243,24 +246,24 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr
testedWord; testedWord;
final WordComposer composer = new WordComposer(); final WordComposer composer = new WordComposer();
final int[] codePoints = StringUtils.toCodePointArray(testedWord); final int[] codePoints = StringUtils.toCodePointArray(testedWord);
final int[] coordinates = keyboard.getCoordinates(codePoints);
composer.setComposingWord(codePoints, coordinates);
final SuggestionResults suggestionResults;
synchronized (mLock) { synchronized (mLock) {
final int[] coordinates = mKeyboard.getCoordinates(codePoints); suggestionResults = dictionaryFacilitator.getSuggestionResults(
composer.setComposingWord(codePoints, coordinates); composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO, keyboard.getProximityInfo(),
final SuggestionResults suggestionResults = dictionaryFacilitator.getSuggestionResults(
composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO, mKeyboard.getProximityInfo(),
settingsValuesForSuggestion, 0 /* sessionId */); settingsValuesForSuggestion, 0 /* sessionId */);
if (suggestionResults.isEmpty()) {
return false;
}
final SuggestedWordInfo firstSuggestion = suggestionResults.first();
final boolean isDistractor = suggestionExceedsDistracterThreshold(
firstSuggestion, consideredWord, DISTRACTER_WORD_SCORE_THRESHOLD);
if (DEBUG) {
Log.d(TAG, "isDistracter: " + isDistractor);
}
return isDistractor;
} }
if (suggestionResults.isEmpty()) {
return false;
}
final SuggestedWordInfo firstSuggestion = suggestionResults.first();
final boolean isDistractor = suggestionExceedsDistracterThreshold(
firstSuggestion, consideredWord, DISTRACTER_WORD_SCORE_THRESHOLD);
if (DEBUG) {
Log.d(TAG, "isDistracter: " + isDistractor);
}
return isDistractor;
} }
private static boolean suggestionExceedsDistracterThreshold(final SuggestedWordInfo suggestion, private static boolean suggestionExceedsDistracterThreshold(final SuggestedWordInfo suggestion,