Support multiple locales in distractor filter.
Bug: 16547557 Change-Id: I4aba278eb4114e0075f3f8b38c7e132743927fa8main
parent
e59f3e4fbf
commit
fdaedb6c1d
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue