Merge "Fix a bug on rotation with selection."
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 096b946..284dadd 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -189,6 +189,7 @@
     private static native void closeNative(long dict);
     private static native int getFormatVersionNative(long dict);
     private static native int getProbabilityNative(long dict, int[] word);
+    private static native int getMaxProbabilityOfExactMatchesNative(long dict, int[] word);
     private static native int getBigramProbabilityNative(long dict, int[] word0,
             boolean isBeginningOfSentence, int[] word1);
     private static native void getWordPropertyNative(long dict, int[] word,
@@ -350,11 +351,17 @@
 
     @Override
     public int getFrequency(final String word) {
-        if (word == null) return NOT_A_PROBABILITY;
+        if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY;
         int[] codePoints = StringUtils.toCodePointArray(word);
         return getProbabilityNative(mNativeDict, codePoints);
     }
 
+    public int getMaxFrequencyOfExactMatches(final String word) {
+        if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY;
+        int[] codePoints = StringUtils.toCodePointArray(word);
+        return getMaxProbabilityOfExactMatchesNative(mNativeDict, codePoints);
+    }
+
     @UsedForTesting
     public boolean isValidNgram(final PrevWordsInfo prevWordsInfo, final String word) {
         return getNgramProbability(prevWordsInfo, word) != NOT_A_PROBABILITY;
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index aab1665..bc7276b 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -95,6 +95,10 @@
         return NOT_A_PROBABILITY;
     }
 
+    public int getMaxFrequencyOfExactMatches(final String word) {
+        return NOT_A_PROBABILITY;
+    }
+
     /**
      * Compares the contents of the character array with the typed word and returns true if they
      * are the same.
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 7fa3d04..e8b0be0 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -63,7 +63,7 @@
     private final Object mLock = new Object();
     private final DistracterFilter mDistracterFilter;
 
-    private static final String[] DICT_TYPES_ORDERED_TO_GET_SUGGESTION =
+    private static final String[] DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS =
             new String[] {
                 Dictionary.TYPE_MAIN,
                 Dictionary.TYPE_USER_HISTORY,
@@ -89,8 +89,8 @@
             new Class[] { Context.class, Locale.class, File.class };
 
     private static final String[] SUB_DICT_TYPES =
-            Arrays.copyOfRange(DICT_TYPES_ORDERED_TO_GET_SUGGESTION, 1 /* start */,
-                    DICT_TYPES_ORDERED_TO_GET_SUGGESTION.length);
+            Arrays.copyOfRange(DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS, 1 /* start */,
+                    DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS.length);
 
     /**
      * Class contains dictionaries for a locale.
@@ -333,7 +333,7 @@
             dictionaries = mDictionaries;
             mDictionaries = new Dictionaries();
         }
-        for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) {
+        for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
             dictionaries.closeDict(dictType);
         }
         mDistracterFilter.close();
@@ -469,7 +469,7 @@
         final SuggestionResults suggestionResults =
                 new SuggestionResults(dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS);
         final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT };
-        for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) {
+        for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
             final Dictionary dictionary = dictionaries.getDict(dictType);
             if (null == dictionary) continue;
             final ArrayList<SuggestedWordInfo> dictionarySuggestions =
@@ -502,7 +502,7 @@
             return false;
         }
         final String lowerCasedWord = word.toLowerCase(dictionaries.mLocale);
-        for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) {
+        for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
             final Dictionary dictionary = dictionaries.getDict(dictType);
             // 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
@@ -516,16 +516,22 @@
         return false;
     }
 
-    public int getFrequency(final String word) {
+    private int getFrequencyInternal(final String word,
+            final boolean isGettingMaxFrequencyOfExactMatches) {
         if (TextUtils.isEmpty(word)) {
             return Dictionary.NOT_A_PROBABILITY;
         }
-        int maxFreq = -1;
+        int maxFreq = Dictionary.NOT_A_PROBABILITY;
         final Dictionaries dictionaries = mDictionaries;
-        for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) {
+        for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
             final Dictionary dictionary = dictionaries.getDict(dictType);
             if (dictionary == null) continue;
-            final int tempFreq = dictionary.getFrequency(word);
+            final int tempFreq;
+            if (isGettingMaxFrequencyOfExactMatches) {
+                tempFreq = dictionary.getMaxFrequencyOfExactMatches(word);
+            } else {
+                tempFreq = dictionary.getFrequency(word);
+            }
             if (tempFreq >= maxFreq) {
                 maxFreq = tempFreq;
             }
@@ -533,6 +539,14 @@
         return maxFreq;
     }
 
+    public int getFrequency(final String word) {
+        return getFrequencyInternal(word, false /* isGettingMaxFrequencyOfExactMatches */);
+    }
+
+    public int getMaxFrequencyOfExactMatches(final String word) {
+        return getFrequencyInternal(word, true /* isGettingMaxFrequencyOfExactMatches */);
+    }
+
     public void clearUserHistoryDictionary() {
         final ExpandableBinaryDictionary userHistoryDict =
                 mDictionaries.getSubDict(Dictionary.TYPE_USER_HISTORY);
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index b10bae0..8664c09 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -441,6 +441,30 @@
         return mBinaryDictionary.isValidWord(word);
     }
 
+    @Override
+    public int getMaxFrequencyOfExactMatches(final String word) {
+        reloadDictionaryIfRequired();
+        boolean lockAcquired = false;
+        try {
+            lockAcquired = mLock.readLock().tryLock(
+                    TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
+            if (lockAcquired) {
+                if (mBinaryDictionary == null) {
+                    return NOT_A_PROBABILITY;
+                }
+                return mBinaryDictionary.getMaxFrequencyOfExactMatches(word);
+            }
+        } catch (final InterruptedException e) {
+            Log.e(TAG, "Interrupted tryLock() in getMaxFrequencyOfExactMatches().", e);
+        } finally {
+            if (lockAcquired) {
+                mLock.readLock().unlock();
+            }
+        }
+        return NOT_A_PROBABILITY;
+    }
+
+
     protected boolean isValidNgramLocked(final PrevWordsInfo prevWordsInfo, final String word) {
         if (mBinaryDictionary == null) return false;
         return mBinaryDictionary.isValidNgram(prevWordsInfo, word);
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index e778a14..378acb3 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -35,7 +35,7 @@
     final public String mTargetApplicationPackageName;
     final public boolean mInputTypeNoAutoCorrect;
     final public boolean mIsPasswordField;
-    final public boolean mIsSettingsSuggestionStripOn;
+    final public boolean mShouldShowSuggestions;
     final public boolean mApplicationSpecifiedCompletionOn;
     final public boolean mShouldInsertSpacesAutomatically;
     final private int mInputType;
@@ -62,7 +62,7 @@
                 Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x"
                         + " imeOptions=0x%08x", inputType, editorInfo.imeOptions));
             }
-            mIsSettingsSuggestionStripOn = false;
+            mShouldShowSuggestions = false;
             mInputTypeNoAutoCorrect = false;
             mApplicationSpecifiedCompletionOn = false;
             mShouldInsertSpacesAutomatically = false;
@@ -81,13 +81,13 @@
 
         // TODO: Have a helper method in InputTypeUtils
         // Make sure that passwords are not displayed in {@link SuggestionStripView}.
-        final boolean noSuggestionStrip = mIsPasswordField
+        final boolean shouldSuppressSuggestions = mIsPasswordField
                 || InputTypeUtils.isEmailVariation(variation)
                 || InputType.TYPE_TEXT_VARIATION_URI == variation
                 || InputType.TYPE_TEXT_VARIATION_FILTER == variation
                 || flagNoSuggestions
                 || flagAutoComplete;
-        mIsSettingsSuggestionStripOn = !noSuggestionStrip;
+        mShouldShowSuggestions = !shouldSuppressSuggestions;
 
         mShouldInsertSpacesAutomatically = InputTypeUtils.isAutoSpaceFriendlyType(inputType);
 
@@ -241,7 +241,7 @@
                 mInputType,
                 (mInputTypeNoAutoCorrect ? " noAutoCorrect" : ""),
                 (mIsPasswordField ? " password" : ""),
-                (mIsSettingsSuggestionStripOn ? " suggestionStrip" : ""),
+                (mShouldShowSuggestions ? " shouldShowSuggestions" : ""),
                 (mApplicationSpecifiedCompletionOn ? " appSpecified" : ""),
                 (mShouldInsertSpacesAutomatically ? " insertSpaces" : ""),
                 mTargetApplicationPackageName);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 4293d4f..deaf6cd 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -602,7 +602,7 @@
         mDictionaryFacilitator.resetDictionaries(this /* context */, locale,
                 settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts,
                 false /* forceReloadMainDictionary */, this);
-        if (settingsValues.mCorrectionEnabled) {
+        if (settingsValues.mAutoCorrectionEnabled) {
             mInputLogic.mSuggest.setAutoCorrectionThreshold(
                     settingsValues.mAutoCorrectionThreshold);
         }
@@ -810,7 +810,7 @@
             mainKeyboardView.closing();
             currentSettingsValues = mSettings.getCurrent();
 
-            if (currentSettingsValues.mCorrectionEnabled) {
+            if (currentSettingsValues.mAutoCorrectionEnabled) {
                 suggest.setAutoCorrectionThreshold(
                         currentSettingsValues.mAutoCorrectionThreshold);
             }
@@ -1406,7 +1406,7 @@
         mInputLogic.mSuggest.getSuggestedWords(mInputLogic.mWordComposer,
                 mInputLogic.mWordComposer.getPrevWordsInfoForSuggestion(),
                 keyboard.getProximityInfo(), currentSettings.mBlockPotentiallyOffensive,
-                currentSettings.mCorrectionEnabled, additionalFeaturesOptions, sessionId,
+                currentSettings.mAutoCorrectionEnabled, additionalFeaturesOptions, sessionId,
                 sequenceNumber, callback);
     }
 
diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
index 8f744be..7989346 100644
--- a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
@@ -102,6 +102,18 @@
     }
 
     @Override
+    public int getMaxFrequencyOfExactMatches(final String word) {
+        if (mLock.readLock().tryLock()) {
+            try {
+                return mBinaryDictionary.getMaxFrequencyOfExactMatches(word);
+            } finally {
+                mLock.readLock().unlock();
+            }
+        }
+        return NOT_A_PROBABILITY;
+    }
+
+    @Override
     public void close() {
         mLock.writeLock().lock();
         try {
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 6c32275..7030ee3 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -778,7 +778,7 @@
         }
         // isComposingWord() may have changed since we stored wasComposing
         if (mWordComposer.isComposingWord()) {
-            if (settingsValues.mCorrectionEnabled) {
+            if (settingsValues.mAutoCorrectionEnabled) {
                 final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR
                         : StringUtils.newSingleCodePointString(codePoint);
                 commitCurrentAutoCorrection(settingsValues, separator, handler);
@@ -1181,7 +1181,7 @@
         // If correction is not enabled, we don't add words to the user history dictionary.
         // That's to avoid unintended additions in some sensitive fields, or fields that
         // expect to receive non-words.
-        if (!settingsValues.mCorrectionEnabled) return;
+        if (!settingsValues.mAutoCorrectionEnabled) return;
 
         if (TextUtils.isEmpty(suggestion)) return;
         final boolean wasAutoCapitalized =
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 6c6e79e..4cdc6e3 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -84,7 +84,7 @@
     public final int mKeyPreviewPopupDismissDelay;
     private final boolean mAutoCorrectEnabled;
     public final float mAutoCorrectionThreshold;
-    public final boolean mCorrectionEnabled;
+    public final boolean mAutoCorrectionEnabled;
     public final int mSuggestionVisibility;
     public final int mDisplayOrientation;
     private final AsyncResultHolder<AppWorkaroundsUtils> mAppWorkarounds;
@@ -150,7 +150,7 @@
         mGestureFloatingPreviewTextEnabled = prefs.getBoolean(
                 Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true);
         mPhraseGestureEnabled = Settings.readPhraseGestureEnabled(prefs, res);
-        mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect;
+        mAutoCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect;
         final String showSuggestionsSetting = prefs.getString(
                 Settings.PREF_SHOW_SUGGESTIONS_SETTING,
                 res.getString(R.string.prefs_suggestion_visibility_default_value));
@@ -189,8 +189,8 @@
     }
 
     public boolean isSuggestionsRequested() {
-        return mInputAttributes.mIsSettingsSuggestionStripOn
-                && (mCorrectionEnabled
+        return mInputAttributes.mShouldShowSuggestions
+                && (mAutoCorrectionEnabled
                         || isCurrentOrientationAllowingSuggestionsPerUserSettings());
     }
 
@@ -318,18 +318,18 @@
 
     private static boolean needsToShowVoiceInputKey(final SharedPreferences prefs,
             final Resources res) {
-        if (!prefs.contains(Settings.PREF_VOICE_INPUT_KEY)) {
-            // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to
-            // {@link Settings#PREF_VOICE_INPUT_KEY}.
+        // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to
+        // {@link Settings#PREF_VOICE_INPUT_KEY}.
+        if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) {
             final String voiceModeMain = res.getString(R.string.voice_mode_main);
             final String voiceMode = prefs.getString(
                     Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain);
             final boolean shouldShowVoiceInputKey = voiceModeMain.equals(voiceMode);
-            prefs.edit().putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey).apply();
-        }
-        // Remove the obsolete preference if exists.
-        if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) {
-            prefs.edit().remove(Settings.PREF_VOICE_MODE_OBSOLETE).apply();
+            prefs.edit()
+                    .putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey)
+                    // Remove the obsolete preference if exists.
+                    .remove(Settings.PREF_VOICE_MODE_OBSOLETE)
+                    .apply();
         }
         return prefs.getBoolean(Settings.PREF_VOICE_INPUT_KEY, true);
     }
@@ -390,8 +390,8 @@
         sb.append("" + mAutoCorrectEnabled);
         sb.append("\n   mAutoCorrectionThreshold = ");
         sb.append("" + mAutoCorrectionThreshold);
-        sb.append("\n   mCorrectionEnabled = ");
-        sb.append("" + mCorrectionEnabled);
+        sb.append("\n   mAutoCorrectionEnabled = ");
+        sb.append("" + mAutoCorrectionEnabled);
         sb.append("\n   mSuggestionVisibility = ");
         sb.append("" + mSuggestionVisibility);
         sb.append("\n   mDisplayOrientation = ");
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index d6a6196..bbeb8dd 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -273,6 +273,17 @@
     return dictionary->getProbability(codePoints, wordLength);
 }
 
+static jint latinime_BinaryDictionary_getMaxProbabilityOfExactMatches(
+        JNIEnv *env, jclass clazz, jlong dict, jintArray word) {
+    Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
+    if (!dictionary) return NOT_A_PROBABILITY;
+    const jsize wordLength = env->GetArrayLength(word);
+    int codePoints[wordLength];
+    env->GetIntArrayRegion(word, 0, wordLength, codePoints);
+    // TODO: Implement.
+    return NOT_A_PROBABILITY;
+}
+
 static jint latinime_BinaryDictionary_getBigramProbability(JNIEnv *env, jclass clazz,
         jlong dict, jintArray word0, jboolean isBeginningOfSentence, jintArray word1) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
@@ -634,6 +645,11 @@
         reinterpret_cast<void *>(latinime_BinaryDictionary_getProbability)
     },
     {
+        const_cast<char *>("getMaxProbabilityOfExactMatchesNative"),
+        const_cast<char *>("(J[I)I"),
+        reinterpret_cast<void *>(latinime_BinaryDictionary_getMaxProbabilityOfExactMatches)
+    },
+    {
         const_cast<char *>("getBigramProbabilityNative"),
         const_cast<char *>("(J[IZ[I)I"),
         reinterpret_cast<void *>(latinime_BinaryDictionary_getBigramProbability)