diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
20 files changed, 255 insertions, 188 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 693e1cdcc..2e108756e 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -83,7 +83,6 @@ public final class BinaryDictionary extends Dictionary { public static final String DIR_NAME_SUFFIX_FOR_RECORD_MIGRATION = ".migrating"; private long mNativeDict; - private final Locale mLocale; private final long mDictSize; private final String mDictFilePath; private final boolean mUseFullEditDistance; @@ -117,8 +116,7 @@ public final class BinaryDictionary extends Dictionary { public BinaryDictionary(final String filename, final long offset, final long length, final boolean useFullEditDistance, final Locale locale, final String dictType, final boolean isUpdatable) { - super(dictType); - mLocale = locale; + super(dictType, locale); mDictSize = length; mDictFilePath = filename; mIsUpdatable = isUpdatable; @@ -138,8 +136,7 @@ public final class BinaryDictionary extends Dictionary { public BinaryDictionary(final String filename, final boolean useFullEditDistance, final Locale locale, final String dictType, final long formatVersion, final Map<String, String> attributeMap) { - super(dictType); - mLocale = locale; + super(dictType, locale); mDictSize = 0; mDictFilePath = filename; // On memory dictionary is always updatable. diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index 729481326..02d5eddaa 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -57,6 +57,13 @@ public final class Constants { @SuppressWarnings("dep-ann") public static final String FORCE_ASCII = "forceAscii"; + /** + * The private IME option used to suppress the floating gesture preview for a given text + * field. This overrides the corresponding keyboard settings preference. + * {@link com.android.inputmethod.latin.settings.SettingsValues#mGestureFloatingPreviewTextEnabled} + */ + public static final String NO_FLOATING_GESTURE_PREVIEW = "noGestureFloatingPreview"; + private ImeOption() { // This utility class is not publicly instantiable. } diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index 560ced9c4..2f79c7662 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -16,12 +16,12 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import java.util.ArrayList; +import java.util.Locale; /** * Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key @@ -62,9 +62,12 @@ public abstract class Dictionary { // Contextual dictionary. public static final String TYPE_CONTEXTUAL = "contextual"; public final String mDictType; + // The locale for this dictionary. May be null if unknown (phony dictionary for example). + public final Locale mLocale; - public Dictionary(final String dictType) { + public Dictionary(final String dictType, final Locale locale) { mDictType = dictType; + mLocale = locale; } /** @@ -162,7 +165,7 @@ public abstract class Dictionary { private static class PhonyDictionary extends Dictionary { // This class is not publicly instantiable. private PhonyDictionary(final String type) { - super(type); + super(type, null); } @Override diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index 2b4c54d48..ca5e93714 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -25,6 +25,7 @@ import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Locale; import java.util.concurrent.CopyOnWriteArrayList; /** @@ -34,13 +35,14 @@ public final class DictionaryCollection extends Dictionary { private final String TAG = DictionaryCollection.class.getSimpleName(); protected final CopyOnWriteArrayList<Dictionary> mDictionaries; - public DictionaryCollection(final String dictType) { - super(dictType); + public DictionaryCollection(final String dictType, final Locale locale) { + super(dictType, locale); mDictionaries = new CopyOnWriteArrayList<>(); } - public DictionaryCollection(final String dictType, final Dictionary... dictionaries) { - super(dictType); + public DictionaryCollection(final String dictType, final Locale locale, + final Dictionary... dictionaries) { + super(dictType, locale); if (null == dictionaries) { mDictionaries = new CopyOnWriteArrayList<>(); } else { @@ -49,8 +51,9 @@ public final class DictionaryCollection extends Dictionary { } } - public DictionaryCollection(final String dictType, final Collection<Dictionary> dictionaries) { - super(dictType); + public DictionaryCollection(final String dictType, final Locale locale, + final Collection<Dictionary> dictionaries) { + super(dictType, locale); mDictionaries = new CopyOnWriteArrayList<>(dictionaries); mDictionaries.removeAll(Collections.singleton(null)); } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index 60d6bc3e5..480bd1f85 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -33,6 +33,7 @@ import com.android.inputmethod.latin.personalization.UserHistoryDictionary; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.utils.DistracterFilter; +import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatchesAndSuggestions; import com.android.inputmethod.latin.utils.DistracterFilterCheckingIsInDictionary; import com.android.inputmethod.latin.utils.ExecutorUtils; import com.android.inputmethod.latin.utils.LanguageModelParam; @@ -59,6 +60,7 @@ public class DictionaryFacilitator { // HACK: This threshold is being used when adding a capitalized entry in the User History // dictionary. private static final int CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT = 140; + private static final int MAX_DICTIONARY_FACILITATOR_CACHE_SIZE = 3; private Dictionaries mDictionaries = new Dictionaries(); private boolean mIsUserDictEnabled = false; @@ -66,6 +68,7 @@ public class DictionaryFacilitator { // To synchronize assigning mDictionaries to ensure closing dictionaries. private final Object mLock = new Object(); private final DistracterFilter mDistracterFilter; + private final DictionaryFacilitatorLruCache mFacilitatorCacheForPersonalization; private static final String[] DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS = new String[] { @@ -173,10 +176,14 @@ public class DictionaryFacilitator { public DictionaryFacilitator() { mDistracterFilter = DistracterFilter.EMPTY_DISTRACTER_FILTER; + mFacilitatorCacheForPersonalization = null; } - public DictionaryFacilitator(final DistracterFilter distracterFilter) { - mDistracterFilter = distracterFilter; + public DictionaryFacilitator(final Context context) { + mFacilitatorCacheForPersonalization = new DictionaryFacilitatorLruCache(context, + MAX_DICTIONARY_FACILITATOR_CACHE_SIZE, "" /* dictionaryNamePrefix */); + mDistracterFilter = new DistracterFilterCheckingExactMatchesAndSuggestions(context, + mFacilitatorCacheForPersonalization); } public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) { @@ -351,6 +358,9 @@ public class DictionaryFacilitator { for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { dictionaries.closeDict(dictType); } + if (mFacilitatorCacheForPersonalization != null) { + mFacilitatorCacheForPersonalization.evictAll(); + } mDistracterFilter.close(); } @@ -493,7 +503,7 @@ public class DictionaryFacilitator { final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) { final Dictionaries dictionaries = mDictionaries; final SuggestionResults suggestionResults = - new SuggestionResults(dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS); + new SuggestionResults(SuggestedWords.MAX_SUGGESTIONS); final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT }; for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { final Dictionary dictionary = dictionaries.getDict(dictType); @@ -597,11 +607,15 @@ public class DictionaryFacilitator { } return; } + // TODO: Get locale from personalizationDataChunk.mDetectedLanguage. + final Locale dataChunkLocale = getLocale(); + final DictionaryFacilitator dictionaryFacilitatorForLocale = + mFacilitatorCacheForPersonalization.get(dataChunkLocale); final ArrayList<LanguageModelParam> languageModelParams = LanguageModelParam.createLanguageModelParamsFrom( personalizationDataChunk.mTokens, personalizationDataChunk.mTimestampInSeconds, - this /* dictionaryFacilitator */, spacingAndPunctuations, + dictionaryFacilitatorForLocale, spacingAndPunctuations, new DistracterFilterCheckingIsInDictionary( mDistracterFilter, personalizationDict)); if (languageModelParams == null || languageModelParams.isEmpty()) { diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 59de4f82a..3459b426d 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -50,7 +50,7 @@ public final class DictionaryFactory { final Locale locale, final boolean useFullEditDistance) { if (null == locale) { Log.e(TAG, "No locale defined for dictionary"); - return new DictionaryCollection(Dictionary.TYPE_MAIN, + return new DictionaryCollection(Dictionary.TYPE_MAIN, locale, createReadOnlyBinaryDictionary(context, locale)); } @@ -75,7 +75,7 @@ public final class DictionaryFactory { // If the list is empty, that means we should not use any dictionary (for example, the user // explicitly disabled the main dictionary), so the following is okay. dictList is never // null, but if for some reason it is, DictionaryCollection handles it gracefully. - return new DictionaryCollection(Dictionary.TYPE_MAIN, dictList); + return new DictionaryCollection(Dictionary.TYPE_MAIN, locale, dictList); } /** @@ -188,7 +188,7 @@ public final class DictionaryFactory { public static Dictionary createDictionaryForTest(final AssetFileAddress[] dictionaryList, final boolean useFullEditDistance, Locale locale) { final DictionaryCollection dictionaryCollection = - new DictionaryCollection(Dictionary.TYPE_MAIN); + new DictionaryCollection(Dictionary.TYPE_MAIN, locale); for (final AssetFileAddress address : dictionaryList) { final ReadOnlyBinaryDictionary readOnlyBinaryDictionary = new ReadOnlyBinaryDictionary( address.mFilename, address.mOffset, address.mLength, useFullEditDistance, diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index de384037f..a1dd67f27 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -86,9 +86,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ private final String mDictName; - /** Dictionary locale */ - private final Locale mLocale; - /** Dictionary file */ private final File mDictFile; @@ -137,10 +134,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ public ExpandableBinaryDictionary(final Context context, final String dictName, final Locale locale, final String dictType, final File dictFile) { - super(dictType); + super(dictType, locale); mDictName = dictName; mContext = context; - mLocale = locale; mDictFile = getDictFile(context, dictName, dictFile); mBinaryDictionary = null; mIsReloading = new AtomicBoolean(); diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index ebe436128..782e18255 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import static com.android.inputmethod.latin.Constants.ImeOption.NO_FLOATING_GESTURE_PREVIEW; import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; @@ -42,6 +43,12 @@ public final class InputAttributes { final public boolean mApplicationSpecifiedCompletionOn; final public boolean mShouldInsertSpacesAutomatically; final public boolean mShouldShowVoiceInputKey; + /** + * Whether the floating gesture preview should be disabled. If true, this should override the + * corresponding keyboard settings preference, always suppressing the floating preview text. + * {@link com.android.inputmethod.latin.settings.SettingsValues#mGestureFloatingPreviewTextEnabled} + */ + final public boolean mDisableGestureFloatingPreviewText; final private int mInputType; final private EditorInfo mEditorInfo; final private String mPackageNameForPrivateImeOptions; @@ -76,6 +83,7 @@ public final class InputAttributes { mApplicationSpecifiedCompletionOn = false; mShouldInsertSpacesAutomatically = false; mShouldShowVoiceInputKey = false; + mDisableGestureFloatingPreviewText = false; return; } // inputClass == InputType.TYPE_CLASS_TEXT @@ -107,6 +115,9 @@ public final class InputAttributes { || hasNoMicrophoneKeyOption(); mShouldShowVoiceInputKey = !noMicrophone; + mDisableGestureFloatingPreviewText = InputAttributes.inPrivateImeOptions( + mPackageNameForPrivateImeOptions, NO_FLOATING_GESTURE_PREVIEW, editorInfo); + // If it's a browser edit field and auto correct is not ON explicitly, then // disable auto correction, but keep suggestions on. // If NO_SUGGESTIONS is set, don't do prediction. diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index ee0ff5c0a..c55acd462 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -93,6 +93,7 @@ import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; import com.android.inputmethod.latin.utils.StatsUtils; +import com.android.inputmethod.latin.utils.StatsUtilsManager; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import com.android.inputmethod.latin.utils.ViewLayoutUtils; @@ -130,8 +131,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private final Settings mSettings; private final DictionaryFacilitator mDictionaryFacilitator = - new DictionaryFacilitator( - new DistracterFilterCheckingExactMatchesAndSuggestions(this /* context */)); + new DictionaryFacilitator(this /* context */); // TODO: Move from LatinIME. private final PersonalizationDictionaryUpdater mPersonalizationDictionaryUpdater = new PersonalizationDictionaryUpdater(this /* context */, mDictionaryFacilitator); @@ -158,6 +158,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private final SubtypeSwitcher mSubtypeSwitcher; private final SubtypeState mSubtypeState = new SubtypeState(); private final SpecialKeyDetector mSpecialKeyDetector; + private StatsUtilsManager mStatsUtilsManager; // Object for reacting to adding/removing a dictionary pack. private final BroadcastReceiver mDictionaryPackInstallReceiver = @@ -538,6 +539,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); mSpecialKeyDetector = new SpecialKeyDetector(this); + mStatsUtilsManager = StatsUtilsManager.getInstance(); mIsHardwareAcceleratedDrawingEnabled = InputMethodServiceCompatUtils.enableHardwareAcceleration(this); Log.i(TAG, "Hardware accelerated drawing: " + mIsHardwareAcceleratedDrawingEnabled); @@ -553,8 +555,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen KeyboardSwitcher.init(this); AudioAndHapticFeedbackManager.init(this); AccessibilityUtils.init(this); - StatsUtils.init(this); - + mStatsUtilsManager.onCreate(this /* context */); super.onCreate(); mHandler.onCreate(); @@ -586,7 +587,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen registerReceiver(mDictionaryDumpBroadcastReceiver, dictDumpFilter); DictionaryDecayBroadcastReciever.setUpIntervalAlarmForDictionaryDecaying(this); - StatsUtils.onCreate(mSettings.getCurrent(), mRichImm); } @@ -609,7 +609,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mDictionaryFacilitator.updateEnabledSubtypes(mRichImm.getMyEnabledInputMethodSubtypeList( true /* allowsImplicitlySelectedSubtypes */)); refreshPersonalizationDictionarySession(currentSettingsValues); - StatsUtils.onLoadSettings(currentSettingsValues); + mStatsUtilsManager.onLoadSettings(currentSettingsValues); } private void refreshPersonalizationDictionarySession( @@ -699,7 +699,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen unregisterReceiver(mConnectivityAndRingerModeChangeReceiver); unregisterReceiver(mDictionaryPackInstallReceiver); unregisterReceiver(mDictionaryDumpBroadcastReceiver); - StatsUtils.onDestroy(); + mStatsUtilsManager.onDestroy(); super.onDestroy(); } @@ -1179,10 +1179,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return mInputLogic.getCurrentRecapitalizeState(); } - public Locale getCurrentSubtypeLocale() { - return mSubtypeSwitcher.getCurrentSubtypeLocale(); - } - /** * @param codePoints code points to get coordinates for. * @return x,y coordinates for this keyboard, as a flattened array. @@ -1496,7 +1492,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } final String wordToShow; if (CapsModeUtils.isAutoCapsMode(mInputLogic.mLastComposedWord.mCapitalizedMode)) { - wordToShow = word.toLowerCase(getCurrentSubtypeLocale()); + wordToShow = word.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale()); } else { wordToShow = word; } diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java index 5d4fc5861..ecf25c28b 100644 --- a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java @@ -40,7 +40,7 @@ public final class ReadOnlyBinaryDictionary extends Dictionary { public ReadOnlyBinaryDictionary(final String filename, final long offset, final long length, final boolean useFullEditDistance, final Locale locale, final String dictType) { - super(dictType); + super(dictType, locale); mBinaryDictionary = new BinaryDictionary(filename, offset, length, useFullEditDistance, locale, dictType, false /* isUpdatable */); } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 6779351fd..9e4aa40a2 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -84,7 +84,7 @@ public final class Suggest { private static ArrayList<SuggestedWordInfo> getTransformedSuggestedWordInfoList( final WordComposer wordComposer, final SuggestionResults results, - final int trailingSingleQuotesCount) { + final int trailingSingleQuotesCount, final Locale defaultLocale) { final boolean shouldMakeSuggestionsAllUpperCase = wordComposer.isAllUpperCase() && !wordComposer.isResumed(); final boolean isOnlyFirstCharCapitalized = @@ -96,9 +96,11 @@ public final class Suggest { || 0 != trailingSingleQuotesCount) { for (int i = 0; i < suggestionsCount; ++i) { final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); + final Locale wordLocale = wordInfo.mSourceDict.mLocale; final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( - wordInfo, results.mLocale, shouldMakeSuggestionsAllUpperCase, - isOnlyFirstCharCapitalized, trailingSingleQuotesCount); + wordInfo, null == wordLocale ? defaultLocale : wordLocale, + shouldMakeSuggestionsAllUpperCase, isOnlyFirstCharCapitalized, + trailingSingleQuotesCount); suggestionsContainer.set(i, transformedWordInfo); } } @@ -134,7 +136,7 @@ public final class Suggest { SESSION_ID_TYPING); final ArrayList<SuggestedWordInfo> suggestionsContainer = getTransformedSuggestedWordInfoList(wordComposer, suggestionResults, - trailingSingleQuotesCount); + trailingSingleQuotesCount, mDictionaryFacilitator.getLocale()); final boolean didRemoveTypedWord = SuggestedWordInfo.removeDups(wordComposer.getTypedWord(), suggestionsContainer); @@ -208,6 +210,7 @@ public final class Suggest { final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( wordComposer, prevWordsInfo, proximityInfo, settingsValuesForSuggestion, SESSION_ID_GESTURE); + final Locale defaultLocale = mDictionaryFacilitator.getLocale(); final ArrayList<SuggestedWordInfo> suggestionsContainer = new ArrayList<>(suggestionResults); final int suggestionsCount = suggestionsContainer.size(); @@ -216,9 +219,10 @@ public final class Suggest { if (isFirstCharCapitalized || isAllUpperCase) { for (int i = 0; i < suggestionsCount; ++i) { final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); + final Locale wordlocale = wordInfo.mSourceDict.mLocale; final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( - wordInfo, suggestionResults.mLocale, isAllUpperCase, isFirstCharCapitalized, - 0 /* trailingSingleQuotesCount */); + wordInfo, null == wordlocale ? defaultLocale : wordlocale, isAllUpperCase, + isFirstCharCapitalized, 0 /* trailingSingleQuotesCount */); suggestionsContainer.set(i, transformedWordInfo); } } diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 1eebabece..6bc3da885 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -429,7 +429,7 @@ public class SuggestedWords { */ @UsedForTesting public SuggestedWordInfo getTypedWordInfoOrNull() { - if (this == EMPTY) { + if (SuggestedWords.INDEX_OF_TYPED_WORD >= size()) { return null; } final SuggestedWordInfo info = getInfo(SuggestedWords.INDEX_OF_TYPED_WORD); diff --git a/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java index e4ee42660..123ab208c 100644 --- a/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java +++ b/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java @@ -17,21 +17,16 @@ package com.android.inputmethod.latin; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; import android.os.Process; -import android.preference.PreferenceManager; import android.util.Log; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.IntentCompatUtils; -import com.android.inputmethod.latin.settings.Settings; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; import com.android.inputmethod.latin.setup.LauncherIconVisibilityManager; -import com.android.inputmethod.latin.setup.SetupActivity; import com.android.inputmethod.latin.utils.UncachedInputMethodManagerUtils; /** @@ -58,6 +53,9 @@ import com.android.inputmethod.latin.utils.UncachedInputMethodManagerUtils; * When a multiuser account has been created, {@link Intent#ACTION_USER_INITIALIZE} is received * by this receiver and it checks the whether the setup wizard's icon should be appeared or not on * the launcher depending on which partition this IME is installed. + * + * When the system locale has been changed, {@link Intent#ACTION_LOCALE_CHANGED} is received by + * this receiver and the {@link KeyboardLayoutSet}'s cache is cleared. */ public final class SystemBroadcastReceiver extends BroadcastReceiver { private static final String TAG = SystemBroadcastReceiver.class.getSimpleName(); @@ -67,21 +65,22 @@ public final class SystemBroadcastReceiver extends BroadcastReceiver { final String intentAction = intent.getAction(); if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(intentAction)) { Log.i(TAG, "Package has been replaced: " + context.getPackageName()); - } else if (Intent.ACTION_BOOT_COMPLETED.equals(intentAction)) { - Log.i(TAG, "Boot has been completed"); - } else if (IntentCompatUtils.is_ACTION_USER_INITIALIZE(intentAction)) { - Log.i(TAG, "User initialize"); - } - - LauncherIconVisibilityManager.onReceiveGlobalIntent(intentAction, context); - - if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(intentAction)) { // Need to restore additional subtypes because system always clears additional // subtypes when the package is replaced. RichInputMethodManager.init(context); final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); final InputMethodSubtype[] additionalSubtypes = richImm.getAdditionalSubtypes(context); richImm.setAdditionalInputMethodSubtypes(additionalSubtypes); + LauncherIconVisibilityManager.updateSetupWizardIconVisibility(context); + } else if (Intent.ACTION_BOOT_COMPLETED.equals(intentAction)) { + Log.i(TAG, "Boot has been completed"); + LauncherIconVisibilityManager.updateSetupWizardIconVisibility(context); + } else if (IntentCompatUtils.is_ACTION_USER_INITIALIZE(intentAction)) { + Log.i(TAG, "User initialize"); + LauncherIconVisibilityManager.updateSetupWizardIconVisibility(context); + } else if (Intent.ACTION_LOCALE_CHANGED.equals(intentAction)) { + Log.i(TAG, "System locale changed"); + KeyboardLayoutSet.onSystemLocaleChanged(); } // The process that hosts this broadcast receiver is invoked and remains alive even after diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java index 6d7f53cf0..d53a61654 100644 --- a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java @@ -30,11 +30,14 @@ import android.preference.DialogPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; +import android.support.v4.view.ViewCompat; import android.util.Pair; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import android.widget.ArrayAdapter; @@ -43,6 +46,7 @@ import android.widget.SpinnerAdapter; import android.widget.Toast; import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; +import com.android.inputmethod.compat.ViewCompatUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; @@ -233,6 +237,12 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { mSubtypeLocaleSpinner.setAdapter(mProxy.getSubtypeLocaleAdapter()); mKeyboardLayoutSetSpinner = (Spinner) v.findViewById(R.id.keyboard_layout_set_spinner); mKeyboardLayoutSetSpinner.setAdapter(mProxy.getKeyboardLayoutSetAdapter()); + // All keyboard layout names are in the Latin script and thus left to right. That means + // the view would align them to the left even if the system locale is RTL, but that + // would look strange. To fix this, we align them to the view's start, which will be + // natural for any direction. + ViewCompatUtils.setTextAlignment( + mKeyboardLayoutSetSpinner, ViewCompatUtils.TEXT_ALIGNMENT_VIEW_START); return v; } @@ -398,6 +408,16 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { } @Override + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + final Bundle savedInstanceState) { + final View view = super.onCreateView(inflater, container, savedInstanceState); + // For correct display in RTL locales, we need to set the layout direction of the + // fragment's top view. + ViewCompat.setLayoutDirection(view, ViewCompat.LAYOUT_DIRECTION_LOCALE); + return view; + } + + @Override public void onActivityCreated(final Bundle savedInstanceState) { final Context context = getActivity(); mSubtypeLocaleAdapter = new SubtypeLocaleAdapter(context); @@ -422,7 +442,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { KEY_SUBTYPE_FOR_SUBTYPE_ENABLER); final SubtypePreference subtypePref = (SubtypePreference)findPreference( mSubtypePreferenceKeyForSubtypeEnabler); - mSubtypeEnablerNotificationDialog = createDialog(subtypePref); + mSubtypeEnablerNotificationDialog = createDialog(); mSubtypeEnablerNotificationDialog.show(); } } @@ -476,7 +496,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { if (findDuplicatedSubtype(subtype) == null) { mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); mSubtypePreferenceKeyForSubtypeEnabler = subtypePref.getKey(); - mSubtypeEnablerNotificationDialog = createDialog(subtypePref); + mSubtypeEnablerNotificationDialog = createDialog(); mSubtypeEnablerNotificationDialog.show(); return; } @@ -513,7 +533,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { localeString, keyboardLayoutSetName); } - private AlertDialog createDialog(final SubtypePreference subtypePref) { + private AlertDialog createDialog() { final AlertDialog.Builder builder = new AlertDialog.Builder( DialogUtils.getPlatformDialogThemeContext(getActivity())); builder.setTitle(R.string.custom_input_styles_title) diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java index 63d848e2d..48f4c758c 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java @@ -23,10 +23,16 @@ public final class DebugSettings { "force_physical_keyboard_special_key"; public static final String PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD = "pref_show_ui_to_accept_typed_word"; - public static final String PREF_KEY_PREVIEW_SHOW_UP_START_SCALE = - "pref_key_preview_show_up_start_scale"; - public static final String PREF_KEY_PREVIEW_DISMISS_END_SCALE = - "pref_key_preview_dismiss_end_scale"; + public static final String PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS = + "pref_has_custom_key_preview_animation_params"; + public static final String PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE = + "pref_key_preview_show_up_start_x_scale"; + public static final String PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE = + "pref_key_preview_show_up_start_y_scale"; + public static final String PREF_KEY_PREVIEW_DISMISS_END_X_SCALE = + "pref_key_preview_dismiss_end_x_scale"; + public static final String PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE = + "pref_key_preview_dismiss_end_y_scale"; public static final String PREF_KEY_PREVIEW_SHOW_UP_DURATION = "pref_key_preview_show_up_duration"; public static final String PREF_KEY_PREVIEW_DISMISS_DURATION = diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java index dc2f88aa8..5640e2039 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java @@ -78,12 +78,18 @@ public final class DebugSettingsFragment extends SubScreenFragment res.getInteger(R.integer.config_key_preview_show_up_duration)); setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION, res.getInteger(R.integer.config_key_preview_dismiss_duration)); - setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_SCALE, - ResourceUtils.getFloatFromFraction( - res, R.fraction.config_key_preview_show_up_start_scale)); - setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_SCALE, - ResourceUtils.getFloatFromFraction( - res, R.fraction.config_key_preview_dismiss_end_scale)); + final float defaultKeyPreviewShowUpStartScale = ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_show_up_start_scale); + final float defaultKeyPreviewDismissEndScale = ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_dismiss_end_scale); + setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE, + defaultKeyPreviewShowUpStartScale); + setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE, + defaultKeyPreviewShowUpStartScale); + setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE, + defaultKeyPreviewDismissEndScale); + setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE, + defaultKeyPreviewDismissEndScale); mServiceNeedsRestart = false; mDebugMode = (TwoStatePreference) findPreference(DebugSettings.PREF_DEBUG_MODE); diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 91a2b6776..11291959c 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -104,10 +104,13 @@ public class SettingsValues { // Debug settings public final boolean mIsInternal; + public final boolean mHasCustomKeyPreviewAnimationParams; public final int mKeyPreviewShowUpDuration; public final int mKeyPreviewDismissDuration; - public final float mKeyPreviewShowUpStartScale; - public final float mKeyPreviewDismissEndScale; + public final float mKeyPreviewShowUpStartXScale; + public final float mKeyPreviewShowUpStartYScale; + public final float mKeyPreviewDismissEndXScale; + public final float mKeyPreviewDismissEndYScale; public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res, final InputAttributes inputAttributes) { @@ -162,8 +165,8 @@ public class SettingsValues { autoCorrectionThresholdRawValue); mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res); mGestureTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true); - mGestureFloatingPreviewTextEnabled = prefs.getBoolean( - Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true); + mGestureFloatingPreviewTextEnabled = !mInputAttributes.mDisableGestureFloatingPreviewText + && prefs.getBoolean(Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true); mPhraseGestureEnabled = Settings.readPhraseGestureEnabled(prefs, res); mAutoCorrectionEnabledPerUserSettings = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; @@ -179,20 +182,30 @@ public class SettingsValues { mTextHighlightColorForAddToDictionaryIndicator = res.getColor( R.color.text_decorator_add_to_dictionary_indicator_text_highlight_color); mIsInternal = Settings.isInternal(prefs); + mHasCustomKeyPreviewAnimationParams = prefs.getBoolean( + DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS, false); mKeyPreviewShowUpDuration = Settings.readKeyPreviewAnimationDuration( prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION, res.getInteger(R.integer.config_key_preview_show_up_duration)); mKeyPreviewDismissDuration = Settings.readKeyPreviewAnimationDuration( prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION, res.getInteger(R.integer.config_key_preview_dismiss_duration)); - mKeyPreviewShowUpStartScale = Settings.readKeyPreviewAnimationScale( - prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_SCALE, - ResourceUtils.getFloatFromFraction( - res, R.fraction.config_key_preview_show_up_start_scale)); - mKeyPreviewDismissEndScale = Settings.readKeyPreviewAnimationScale( - prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_SCALE, - ResourceUtils.getFloatFromFraction( - res, R.fraction.config_key_preview_dismiss_end_scale)); + final float defaultKeyPreviewShowUpStartScale = ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_show_up_start_scale); + final float defaultKeyPreviewDismissEndScale = ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_dismiss_end_scale); + mKeyPreviewShowUpStartXScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE, + defaultKeyPreviewShowUpStartScale); + mKeyPreviewShowUpStartYScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE, + defaultKeyPreviewShowUpStartScale); + mKeyPreviewDismissEndXScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE, + defaultKeyPreviewDismissEndScale); + mKeyPreviewDismissEndYScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE, + defaultKeyPreviewDismissEndScale); mDisplayOrientation = res.getConfiguration().orientation; mAppWorkarounds = new AsyncResultHolder<>(); final PackageInfo packageInfo = TargetPackageInfoGetterTask.getCachedPackageInfo( @@ -429,10 +442,14 @@ public class SettingsValues { sb.append("" + mKeyPreviewShowUpDuration); sb.append("\n mKeyPreviewDismissDuration = "); sb.append("" + mKeyPreviewDismissDuration); - sb.append("\n mKeyPreviewShowUpStartScale = "); - sb.append("" + mKeyPreviewShowUpStartScale); - sb.append("\n mKeyPreviewDismissEndScale = "); - sb.append("" + mKeyPreviewDismissEndScale); + sb.append("\n mKeyPreviewShowUpStartScaleX = "); + sb.append("" + mKeyPreviewShowUpStartXScale); + sb.append("\n mKeyPreviewShowUpStartScaleY = "); + sb.append("" + mKeyPreviewShowUpStartYScale); + sb.append("\n mKeyPreviewDismissEndScaleX = "); + sb.append("" + mKeyPreviewDismissEndXScale); + sb.append("\n mKeyPreviewDismissEndScaleY = "); + sb.append("" + mKeyPreviewDismissEndYScale); return sb.toString(); } } diff --git a/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java b/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java index 9585736e7..3f0b10225 100644 --- a/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java +++ b/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java @@ -24,7 +24,6 @@ import android.content.pm.PackageManager; import android.preference.PreferenceManager; import android.util.Log; -import com.android.inputmethod.compat.IntentCompatUtils; import com.android.inputmethod.latin.settings.Settings; /** @@ -55,14 +54,6 @@ import com.android.inputmethod.latin.settings.Settings; public final class LauncherIconVisibilityManager { private static final String TAG = LauncherIconVisibilityManager.class.getSimpleName(); - public static void onReceiveGlobalIntent(final String action, final Context context) { - if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(action) || - Intent.ACTION_BOOT_COMPLETED.equals(action) || - IntentCompatUtils.is_ACTION_USER_INITIALIZE(action)) { - updateSetupWizardIconVisibility(context); - } - } - public static void updateSetupWizardIconVisibility(final Context context) { final ComponentName setupWizardActivity = new ComponentName(context, SetupActivity.class); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java index 2207ffea9..e10571e4a 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java @@ -20,13 +20,14 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.ConcurrentHashMap; import android.content.Context; import android.content.res.Resources; import android.text.InputType; import android.util.Log; import android.util.LruCache; +import android.util.Pair; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; @@ -34,6 +35,7 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardLayoutSet; import com.android.inputmethod.latin.DictionaryFacilitator; +import com.android.inputmethod.latin.DictionaryFacilitatorLruCache; import com.android.inputmethod.latin.PrevWordsInfo; import com.android.inputmethod.latin.RichInputMethodSubtype; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; @@ -49,15 +51,15 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr DistracterFilterCheckingExactMatchesAndSuggestions.class.getSimpleName(); private static final boolean DEBUG = false; - private static final long TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS = 120; - private static final int MAX_DISTRACTERS_CACHE_SIZE = 512; + private static final int MAX_DISTRACTERS_CACHE_SIZE = 1024; private final Context mContext; - private final Map<Locale, InputMethodSubtype> mLocaleToSubtypeMap; - private final Map<Locale, Keyboard> mLocaleToKeyboardMap; - private final DictionaryFacilitator mDictionaryFacilitator; - private final LruCache<String, Boolean> mDistractersCache; - private Keyboard mKeyboard; + private final ConcurrentHashMap<Locale, InputMethodSubtype> mLocaleToSubtypeCache; + private final ConcurrentHashMap<Locale, Keyboard> mLocaleToKeyboardCache; + private final DictionaryFacilitatorLruCache mDictionaryFacilitatorLruCache; + // The key is a pair of a locale and a word. The value indicates the word is a distracter to + // words of the locale. + private final LruCache<Pair<Locale, String>, Boolean> mDistractersCache; private final Object mLock = new Object(); // If the score of the top suggestion exceeds this value, the tested word (e.g., @@ -71,19 +73,23 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr * Create a DistracterFilter instance. * * @param context the context. + * @param dictionaryFacilitatorLruCache the cache of dictionaryFacilitators that are used for + * checking distracters. */ - public DistracterFilterCheckingExactMatchesAndSuggestions(final Context context) { + public DistracterFilterCheckingExactMatchesAndSuggestions(final Context context, + final DictionaryFacilitatorLruCache dictionaryFacilitatorLruCache) { mContext = context; - mLocaleToSubtypeMap = new HashMap<>(); - mLocaleToKeyboardMap = new HashMap<>(); - mDictionaryFacilitator = new DictionaryFacilitator(); + mLocaleToSubtypeCache = new ConcurrentHashMap<>(); + mLocaleToKeyboardCache = new ConcurrentHashMap<>(); + mDictionaryFacilitatorLruCache = dictionaryFacilitatorLruCache; mDistractersCache = new LruCache<>(MAX_DISTRACTERS_CACHE_SIZE); - mKeyboard = null; } @Override public void close() { - mDictionaryFacilitator.closeDictionaries(); + mLocaleToSubtypeCache.clear(); + mLocaleToKeyboardCache.clear(); + mDistractersCache.evictAll(); } @Override @@ -100,29 +106,36 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr newLocaleToSubtypeMap.put(locale, subtype); } } - if (mLocaleToSubtypeMap.equals(newLocaleToSubtypeMap)) { + if (mLocaleToSubtypeCache.equals(newLocaleToSubtypeMap)) { // Enabled subtypes have not been changed. return; } - synchronized (mLock) { - mLocaleToSubtypeMap.clear(); - mLocaleToSubtypeMap.putAll(newLocaleToSubtypeMap); - mLocaleToKeyboardMap.clear(); + // Update subtype and keyboard map for locales that are in the current mapping. + for (final Locale locale: mLocaleToSubtypeCache.keySet()) { + if (newLocaleToSubtypeMap.containsKey(locale)) { + 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) { - final Keyboard cachedKeyboard = mLocaleToKeyboardMap.get(newLocale); + private Keyboard getKeyboardForLocale(final Locale locale) { + final Keyboard cachedKeyboard = mLocaleToKeyboardCache.get(locale); if (cachedKeyboard != null) { - mKeyboard = cachedKeyboard; - return; - } - final InputMethodSubtype subtype; - synchronized (mLock) { - subtype = mLocaleToSubtypeMap.get(newLocale); + return cachedKeyboard; } + final InputMethodSubtype subtype = mLocaleToSubtypeCache.get(locale); if (subtype == null) { - return; + return null; } final EditorInfo editorInfo = new EditorInfo(); editorInfo.inputType = InputType.TYPE_CLASS_TEXT; @@ -135,15 +148,9 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr builder.setSubtype(new RichInputMethodSubtype(subtype)); builder.setIsSpellChecker(false /* isSpellChecker */); final KeyboardLayoutSet layoutSet = builder.build(); - mKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); - } - - private void loadDictionariesForLocale(final Locale newlocale) throws InterruptedException { - mDictionaryFacilitator.resetDictionaries(mContext, newlocale, - false /* useContactsDict */, false /* usePersonalizedDicts */, - false /* forceReloadMainDictionary */, null /* listener */); - mDictionaryFacilitator.waitForLoadingMainDictionary( - TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS, TimeUnit.SECONDS); + final Keyboard newKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); + mLocaleToKeyboardCache.put(locale, newKeyboard); + return newKeyboard; } /** @@ -161,30 +168,18 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr if (locale == null) { return false; } - if (!locale.equals(mDictionaryFacilitator.getLocale())) { - synchronized (mLock) { - if (!mLocaleToSubtypeMap.containsKey(locale)) { - Log.e(TAG, "Locale " + locale + " is not enabled."); - // TODO: Investigate what we should do for disabled locales. - return false; - } - loadKeyboardForLocale(locale); - // Reset dictionaries for the locale. - try { - mDistractersCache.evictAll(); - loadDictionariesForLocale(locale); - } catch (final InterruptedException e) { - Log.e(TAG, "Interrupted while waiting for loading dicts in DistracterFilter", - e); - return false; - } - } + if (!mLocaleToSubtypeCache.containsKey(locale)) { + Log.e(TAG, "Locale " + locale + " is not enabled."); + // TODO: Investigate what we should do for disabled locales. + return false; } - + final DictionaryFacilitator dictionaryFacilitator = + mDictionaryFacilitatorLruCache.get(locale); if (DEBUG) { 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 (DEBUG) { Log.d(TAG, "isDistracter: true (cache hit)"); @@ -193,13 +188,13 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr } final boolean isDistracterCheckedByGetMaxFreqencyOfExactMatches = - checkDistracterUsingMaxFreqencyOfExactMatches(testedWord); + checkDistracterUsingMaxFreqencyOfExactMatches(dictionaryFacilitator, testedWord); if (isDistracterCheckedByGetMaxFreqencyOfExactMatches) { - // Add the word to the cache. - mDistractersCache.put(testedWord, Boolean.TRUE); + // Add the pair of locale and word to the cache. + mDistractersCache.put(cacheKey, Boolean.TRUE); return true; } - final boolean isValidWord = mDictionaryFacilitator.isValidWord(testedWord, + final boolean isValidWord = dictionaryFacilitator.isValidWord(testedWord, false /* ignoreCase */); if (isValidWord) { // Valid word is not a distractor. @@ -209,21 +204,23 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr return false; } + final Keyboard keyboard = getKeyboardForLocale(locale); final boolean isDistracterCheckedByGetSuggestion = - checkDistracterUsingGetSuggestions(testedWord); + checkDistracterUsingGetSuggestions(dictionaryFacilitator, keyboard, testedWord); if (isDistracterCheckedByGetSuggestion) { - // Add the word to the cache. - mDistractersCache.put(testedWord, Boolean.TRUE); + // Add the pair of locale and word to the cache. + mDistractersCache.put(cacheKey, Boolean.TRUE); return true; } return false; } - private boolean checkDistracterUsingMaxFreqencyOfExactMatches(final String testedWord) { + private static boolean checkDistracterUsingMaxFreqencyOfExactMatches( + final DictionaryFacilitator dictionaryFacilitator, final String testedWord) { // The tested word is a distracter when there is a word that is exact matched to the tested // word and its probability is higher than the tested word's probability. - final int perfectMatchFreq = mDictionaryFacilitator.getFrequency(testedWord); - final int exactMatchFreq = mDictionaryFacilitator.getMaxFrequencyOfExactMatches(testedWord); + final int perfectMatchFreq = dictionaryFacilitator.getFrequency(testedWord); + final int exactMatchFreq = dictionaryFacilitator.getMaxFrequencyOfExactMatches(testedWord); final boolean isDistracter = perfectMatchFreq < exactMatchFreq; if (DEBUG) { Log.d(TAG, "perfectMatchFreq: " + perfectMatchFreq); @@ -233,8 +230,10 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr return isDistracter; } - private boolean checkDistracterUsingGetSuggestions(final String testedWord) { - if (mKeyboard == null) { + private boolean checkDistracterUsingGetSuggestions( + final DictionaryFacilitator dictionaryFacilitator, final Keyboard keyboard, + final String testedWord) { + if (keyboard == null) { return false; } final SettingsValuesForSuggestion settingsValuesForSuggestion = @@ -247,24 +246,24 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr testedWord; final WordComposer composer = new WordComposer(); final int[] codePoints = StringUtils.toCodePointArray(testedWord); - + final int[] coordinates = keyboard.getCoordinates(codePoints); + composer.setComposingWord(codePoints, coordinates); + final SuggestionResults suggestionResults; synchronized (mLock) { - final int[] coordinates = mKeyboard.getCoordinates(codePoints); - composer.setComposingWord(codePoints, coordinates); - final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( - composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO, mKeyboard.getProximityInfo(), + suggestionResults = dictionaryFacilitator.getSuggestionResults( + composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO, keyboard.getProximityInfo(), 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, diff --git a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java index 7170bd789..eaa5743d4 100644 --- a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java +++ b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java @@ -30,18 +30,16 @@ import java.util.TreeSet; * than its limit */ public final class SuggestionResults extends TreeSet<SuggestedWordInfo> { - public final Locale mLocale; public final ArrayList<SuggestedWordInfo> mRawSuggestions; private final int mCapacity; - public SuggestionResults(final Locale locale, final int capacity) { - this(locale, sSuggestedWordInfoComparator, capacity); + public SuggestionResults(final int capacity) { + this(sSuggestedWordInfoComparator, capacity); } - public SuggestionResults(final Locale locale, final Comparator<SuggestedWordInfo> comparator, + public SuggestionResults(final Comparator<SuggestedWordInfo> comparator, final int capacity) { super(comparator); - mLocale = locale; mCapacity = capacity; if (ProductionFlags.INCLUDE_RAW_SUGGESTIONS) { mRawSuggestions = new ArrayList<>(); |