Merge "Remove unused default object of GestureStrokePreviewParams"
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index 55282c5..b8d1651 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -23,8 +23,10 @@
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
 
+import com.android.inputmethod.latin.Dictionary;
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 
@@ -66,30 +68,42 @@
     }
 
     public static CharSequence getTextWithSuggestionSpan(final Context context,
-            final String pickedWord, final SuggestedWords suggestedWords,
-            final boolean dictionaryAvailable) {
-        if (!dictionaryAvailable || TextUtils.isEmpty(pickedWord) || suggestedWords.isEmpty()
+            final String pickedWord, final SuggestedWords suggestedWords) {
+        if (TextUtils.isEmpty(pickedWord) || suggestedWords.isEmpty()
                 || suggestedWords.mIsPrediction || suggestedWords.mIsPunctuationSuggestions) {
             return pickedWord;
         }
 
-        final Spannable spannable = new SpannableString(pickedWord);
+        boolean hasSuggestionFromMainDictionary = false;
         final ArrayList<String> suggestionsList = CollectionUtils.newArrayList();
         for (int i = 0; i < suggestedWords.size(); ++i) {
             if (suggestionsList.size() >= SuggestionSpan.SUGGESTIONS_MAX_SIZE) {
                 break;
             }
+            final SuggestedWordInfo info = suggestedWords.getInfo(i);
+            if (info.mKind == SuggestedWordInfo.KIND_PREDICTION) {
+                continue;
+            }
+            if (info.mSourceDict.mDictType == Dictionary.TYPE_MAIN) {
+                hasSuggestionFromMainDictionary = true;
+            }
             final String word = suggestedWords.getWord(i);
             if (!TextUtils.equals(pickedWord, word)) {
                 suggestionsList.add(word.toString());
             }
         }
+        if (!hasSuggestionFromMainDictionary) {
+            // If we don't have any suggestions from the dictionary, it probably looks bad
+            // enough as it is already because suggestions come pretty much only from contacts.
+            // Let's not embed these bad suggestions in the text view so as to avoid using
+            // them with recorrection.
+            return pickedWord;
+        }
 
-        // TODO: We should avoid adding suggestion span candidates that came from the bigram
-        // prediction.
         final SuggestionSpan suggestionSpan = new SuggestionSpan(context, null /* locale */,
                 suggestionsList.toArray(new String[suggestionsList.size()]), 0 /* flags */,
                 SuggestionSpanPickedNotificationReceiver.class);
+        final Spannable spannable = new SpannableString(pickedWord);
         spannable.setSpan(suggestionSpan, 0, pickedWord.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
         return spannable;
     }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index e9988bf..6054d29 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -151,7 +151,6 @@
     private final SubtypeSwitcher mSubtypeSwitcher;
     private final SubtypeState mSubtypeState = new SubtypeState();
 
-    private boolean mIsMainDictionaryAvailable;
     private UserBinaryDictionary mUserDictionary;
     private boolean mIsUserDictionaryAvailable;
 
@@ -555,7 +554,6 @@
     // Note that this method is called from a non-UI thread.
     @Override
     public void onUpdateMainDictionaryAvailability(final boolean isMainDictionaryAvailable) {
-        mIsMainDictionaryAvailable = isMainDictionaryAvailable;
         final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
         if (mainKeyboardView != null) {
             mainKeyboardView.setMainDictionaryAvailability(isMainDictionaryAvailable);
@@ -586,7 +584,6 @@
             newSuggest.setAutoCorrectionThreshold(settingsValues.mAutoCorrectionThreshold);
         }
 
-        mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
             ResearchLogger.getInstance().initSuggest(newSuggest);
         }
@@ -605,7 +602,6 @@
         final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
         mInputLogic.mSuggest.resetMainDict(this, subtypeLocale,
                 this /* SuggestInitializationListener */);
-        mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
     }
 
     @Override
@@ -637,7 +633,7 @@
             mDisplayOrientation = conf.orientation;
             mHandler.startOrientationChanging();
             mInputLogic.mConnection.beginBatchEdit();
-            mInputLogic.commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+            mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR);
             mInputLogic.mConnection.finishComposingText();
             mInputLogic.mConnection.endBatchEdit();
             if (isShowingOptionDialog()) {
@@ -858,7 +854,8 @@
         mHandler.cancelUpdateSuggestionStrip();
         mHandler.cancelDoubleSpacePeriodTimer();
 
-        mainKeyboardView.setMainDictionaryAvailability(mIsMainDictionaryAvailable);
+        mainKeyboardView.setMainDictionaryAvailability(null != suggest
+                ? suggest.hasMainDictionary() : false);
         mainKeyboardView.setKeyPreviewPopupEnabled(currentSettingsValues.mKeyPreviewPopupOn,
                 currentSettingsValues.mKeyPreviewPopupDismissDelay);
         mainKeyboardView.setSlidingKeyInputPreviewEnabled(
@@ -1402,7 +1399,7 @@
                 mInputLogic.commitCurrentAutoCorrection(currentSettingsValues,
                         LastComposedWord.NOT_A_SEPARATOR, mHandler);
             } else {
-                mInputLogic.commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+                mInputLogic.commitTyped(currentSettingsValues, LastComposedWord.NOT_A_SEPARATOR);
             }
         }
         final int codePointBeforeCursor = mInputLogic.mConnection.getCodePointBeforeCursor();
@@ -1692,7 +1689,7 @@
     // TODO[IL]: Rename this to avoid using handle*
     private void handleClose() {
         // TODO: Verify that words are logged properly when IME is closed.
-        mInputLogic.commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+        mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR);
         requestHideSelf(0);
         final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
         if (mainKeyboardView != null) {
@@ -1979,8 +1976,8 @@
         // typed word.
         final String replacedWord = mInputLogic.mWordComposer.getTypedWord();
         LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion, index, suggestedWords);
-        commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
-                LastComposedWord.NOT_A_SEPARATOR);
+        mInputLogic.commitChosenWord(currentSettings, suggestion,
+                LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR);
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
             ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion,
                     mInputLogic.mWordComposer.isBatchMode(), suggestionInfo.mScore,
@@ -2018,38 +2015,6 @@
         }
     }
 
-    /**
-     * Commits the chosen word to the text field and saves it for later retrieval.
-     */
-    // TODO[IL]: Move to InputLogic and make public again
-    public void commitChosenWord(final String chosenWord, final int commitType,
-            final String separatorString) {
-        final SuggestedWords suggestedWords = mInputLogic.mSuggestedWords;
-        mInputLogic.mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
-                this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1);
-        // Add the word to the user history dictionary
-        final String prevWord = addToUserHistoryDictionary(chosenWord);
-        // TODO: figure out here if this is an auto-correct or if the best word is actually
-        // what user typed. Note: currently this is done much later in
-        // LastComposedWord#didCommitTypedWord by string equality of the remembered
-        // strings.
-        mInputLogic.mLastComposedWord = mInputLogic.mWordComposer.commitWord(commitType,
-                chosenWord, separatorString, prevWord);
-        final boolean shouldDiscardPreviousWordForSuggestion;
-        if (0 == StringUtils.codePointCount(separatorString)) {
-            // Separator is 0-length. Discard the word only if the current language has spaces.
-            shouldDiscardPreviousWordForSuggestion =
-                    mSettings.getCurrent().mCurrentLanguageHasSpaces;
-        } else {
-            // Otherwise, we discard if the separator contains any non-whitespace.
-            shouldDiscardPreviousWordForSuggestion =
-                    !StringUtils.containsOnlyWhitespace(separatorString);
-        }
-        if (shouldDiscardPreviousWordForSuggestion) {
-            mInputLogic.mWordComposer.discardPreviousWordForSuggestion();
-        }
-    }
-
     // TODO[IL]: Define a clean interface for this
     public void setPunctuationSuggestions() {
         final SettingsValues currentSettings = mSettings.getCurrent();
@@ -2062,38 +2027,6 @@
         setSuggestionStripShown(isSuggestionsStripVisible());
     }
 
-    private String addToUserHistoryDictionary(final String suggestion) {
-        if (TextUtils.isEmpty(suggestion)) return null;
-        final Suggest suggest = mInputLogic.mSuggest;
-        if (suggest == null) return null;
-
-        // 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.
-        final SettingsValues currentSettings = mSettings.getCurrent();
-        if (!currentSettings.mCorrectionEnabled) return null;
-
-        final UserHistoryDictionary userHistoryDictionary = suggest.getUserHistoryDictionary();
-        if (userHistoryDictionary == null) return null;
-
-        final String prevWord = mInputLogic.mConnection.getNthPreviousWord(currentSettings, 2);
-        final String secondWord;
-        if (mInputLogic.mWordComposer.wasAutoCapitalized()
-                && !mInputLogic.mWordComposer.isMostlyCaps()) {
-            secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
-        } else {
-            secondWord = suggestion;
-        }
-        // We demote unrecognized words (frequency < 0, below) by specifying them as "invalid".
-        // We don't add words with 0-frequency (assuming they would be profanity etc.).
-        final int maxFreq = AutoCorrectionUtils.getMaxFrequency(
-                suggest.getUnigramDictionaries(), suggestion);
-        if (maxFreq == 0) return null;
-        userHistoryDictionary.addToDictionary(prevWord, secondWord, maxFreq > 0,
-                (int)TimeUnit.MILLISECONDS.toSeconds((System.currentTimeMillis())));
-        return prevWord;
-    }
-
     private boolean isResumableWord(final String word, final SettingsValues settings) {
         final int firstCodePoint = word.codePointAt(0);
         return settings.isWordCodePoint(firstCodePoint)
@@ -2356,7 +2289,7 @@
     public void launchKeyboardedDialogActivity(final Class<? extends Activity> activityClass) {
         // Put the text in the attached EditText into a safe, saved state before switching to a
         // new activity that will also use the soft keyboard.
-        mInputLogic.commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+        mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR);
         launchSubActivity(activityClass);
     }
 
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 5a70d64..a433ca6 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -39,8 +39,10 @@
 import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.WordComposer;
 import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.latin.personalization.UserHistoryDictionary;
 import com.android.inputmethod.latin.settings.Settings;
 import com.android.inputmethod.latin.settings.SettingsValues;
+import com.android.inputmethod.latin.utils.AutoCorrectionUtils;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.InputTypeUtils;
 import com.android.inputmethod.latin.utils.LatinImeLoggerUtils;
@@ -48,7 +50,9 @@
 import com.android.inputmethod.latin.utils.StringUtils;
 import com.android.inputmethod.research.ResearchLogger;
 
+import java.util.Locale;
 import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
 
 /**
  * This class manages the input logic.
@@ -284,7 +288,7 @@
                     // first so that we can insert the character at the current cursor position.
                     resetEntireInputState(settingsValues, mLastSelectionStart, mLastSelectionEnd);
                 } else {
-                    commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+                    commitTyped(settingsValues, LastComposedWord.NOT_A_SEPARATOR);
                 }
             }
             final int keyX, keyY;
@@ -428,7 +432,7 @@
                 commitCurrentAutoCorrection(settingsValues, separator, handler);
                 didAutoCorrect = true;
             } else {
-                commitTyped(StringUtils.newSingleCodePointString(codePoint));
+                commitTyped(settingsValues, StringUtils.newSingleCodePointString(codePoint));
             }
         }
 
@@ -811,6 +815,38 @@
         keyboardSwitcher.updateShiftState();
     }
 
+    private String performAdditionToUserHistoryDictionary(final SettingsValues settingsValues,
+            final String suggestion) {
+        // 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 null;
+
+        if (TextUtils.isEmpty(suggestion)) return null;
+        final Suggest suggest = mSuggest;
+        if (suggest == null) return null;
+
+        final UserHistoryDictionary userHistoryDictionary = suggest.getUserHistoryDictionary();
+        if (userHistoryDictionary == null) return null;
+
+        final String prevWord = mConnection.getNthPreviousWord(settingsValues, 2);
+        final String secondWord;
+        if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
+            secondWord = suggestion.toLowerCase(settingsValues.mLocale);
+        } else {
+            secondWord = suggestion;
+        }
+        // We demote unrecognized words (frequency < 0, below) by specifying them as "invalid".
+        // We don't add words with 0-frequency (assuming they would be profanity etc.).
+        final int maxFreq = AutoCorrectionUtils.getMaxFrequency(
+                suggest.getUnigramDictionaries(), suggestion);
+        if (maxFreq == 0) return null;
+        userHistoryDictionary.addToDictionary(prevWord, secondWord, maxFreq > 0,
+                (int)TimeUnit.MILLISECONDS.toSeconds((System.currentTimeMillis())));
+        return prevWord;
+    }
+
+
     /**
      * Check if the cursor is actually at the end of a word. If so, restart suggestions on this
      * word, otherwise do nothing.
@@ -1151,19 +1187,19 @@
      * user presses the Send button for an SMS, we don't auto-correct as that would be unexpected.
      * In this case, `separatorString' is set to NOT_A_SEPARATOR.
      *
-     * @param separatorString the separator string that's causing the commit, or NOT_A_SEPARATOR if
-     *   none.
+     * @param settingsValues the current values of the settings.
+     * @param separatorString the separator that's causing the commit, or NOT_A_SEPARATOR if none.
      */
     // TODO: Make this private
-    public void commitTyped(final String separatorString) {
+    public void commitTyped(final SettingsValues settingsValues, final String separatorString) {
         if (!mWordComposer.isComposingWord()) return;
         final String typedWord = mWordComposer.getTypedWord();
         if (typedWord.length() > 0) {
             if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                 ResearchLogger.getInstance().onWordFinished(typedWord, mWordComposer.isBatchMode());
             }
-            mLatinIME.commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD,
-                    separatorString);
+            commitChosenWord(settingsValues, typedWord,
+                    LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, separatorString);
         }
     }
 
@@ -1210,8 +1246,8 @@
                 ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection,
                         separator, mWordComposer.isBatchMode(), suggestedWords);
             }
-            mLatinIME.commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
-                    separator);
+            commitChosenWord(settingsValues, autoCorrection,
+                    LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separator);
             if (!typedWord.equals(autoCorrection)) {
                 // This will make the correction flash for a short while as a visual clue
                 // to the user that auto-correction happened. It has no other effect; in particular
@@ -1225,4 +1261,40 @@
             }
         }
     }
+
+    /**
+     * Commits the chosen word to the text field and saves it for later retrieval.
+     *
+     * @param settingsValues the current values of the settings.
+     * @param chosenWord the word we want to commit.
+     * @param commitType the type of the commit, as one of LastComposedWord.COMMIT_TYPE_*
+     * @param separatorString the separator that's causing the commit, or NOT_A_SEPARATOR if none.
+     */
+    // TODO: Make this private
+    public void commitChosenWord(final SettingsValues settingsValues, final String chosenWord,
+            final int commitType, final String separatorString) {
+        final SuggestedWords suggestedWords = mSuggestedWords;
+        mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(mLatinIME, chosenWord,
+                suggestedWords), 1);
+        // Add the word to the user history dictionary
+        final String prevWord = performAdditionToUserHistoryDictionary(settingsValues, chosenWord);
+        // TODO: figure out here if this is an auto-correct or if the best word is actually
+        // what user typed. Note: currently this is done much later in
+        // LastComposedWord#didCommitTypedWord by string equality of the remembered
+        // strings.
+        mLastComposedWord = mWordComposer.commitWord(commitType,
+                chosenWord, separatorString, prevWord);
+        final boolean shouldDiscardPreviousWordForSuggestion;
+        if (0 == StringUtils.codePointCount(separatorString)) {
+            // Separator is 0-length. Discard the word only if the current language has spaces.
+            shouldDiscardPreviousWordForSuggestion = settingsValues.mCurrentLanguageHasSpaces;
+        } else {
+            // Otherwise, we discard if the separator contains any non-whitespace.
+            shouldDiscardPreviousWordForSuggestion =
+                    !StringUtils.containsOnlyWhitespace(separatorString);
+        }
+        if (shouldDiscardPreviousWordForSuggestion) {
+            mWordComposer.discardPreviousWordForSuggestion();
+        }
+    }
 }