Always show the typed word in recorrections.

Bug: 11330140
Bug: 17875601
Bug: 17623275
Change-Id: Ie4620f36f312c54c7b01b5f6cbdb0bc9171b6179
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index d21fa16..743b570 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1627,7 +1627,7 @@
     }
 
     @Override
-    public void showAddToDictionaryHint(final String word) {
+    public void suggestAddingToDictionary(final String word, final boolean isFromSuggestionStrip) {
         if (!hasSuggestionStripView()) {
             return;
         }
@@ -1637,7 +1637,8 @@
         } else {
             wordToShow = word;
         }
-        mSuggestionStripView.showAddToDictionaryHint(wordToShow);
+        mSuggestionStripView.showAddToDictionaryHint(wordToShow,
+                isFromSuggestionStrip /* shouldShowWordToSave */);
     }
 
     // This will show either an empty suggestion strip (if prediction is enabled) or
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 4665764..e0c0692 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -413,28 +413,6 @@
         return isPrediction(mInputStyle);
     }
 
-    // SuggestedWords is an immutable object, as much as possible. We must not just remove
-    // words from the member ArrayList as some other parties may expect the object to never change.
-    // This is only ever called by recorrection at the moment, hence the ForRecorrection moniker.
-    public SuggestedWords getSuggestedWordsExcludingTypedWordForRecorrection() {
-        final ArrayList<SuggestedWordInfo> newSuggestions = new ArrayList<>();
-        String typedWord = null;
-        for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) {
-            final SuggestedWordInfo info = mSuggestedWordInfoList.get(i);
-            if (!info.isKindOf(SuggestedWordInfo.KIND_TYPED)) {
-                newSuggestions.add(info);
-            } else {
-                assert(null == typedWord);
-                typedWord = info.mWord;
-            }
-        }
-        // We should never autocorrect, so we say the typed word is valid. Also, in this case,
-        // no auto-correction should take place hence willAutoCorrect = false.
-        return new SuggestedWords(newSuggestions, null /* rawSuggestions */, typedWord,
-                true /* typedWordValid */, false /* willAutoCorrect */, mIsObsoleteSuggestions,
-                SuggestedWords.INPUT_STYLE_RECORRECTION, NOT_A_SEQUENCE_NUMBER);
-    }
-
     // Creates a new SuggestedWordInfo from the currently suggested words that removes all but the
     // last word of all suggestions, separated by a space. This is necessary because when we commit
     // a multiple-word suggestion, the IME only retains the last word as the composing word, and
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index f23ce7f..f67b8de 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -349,7 +349,8 @@
         inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
 
         if (shouldShowAddToDictionaryHint) {
-            mSuggestionStripViewAccessor.showAddToDictionaryHint(suggestion);
+            mSuggestionStripViewAccessor.suggestAddingToDictionary(suggestion,
+                    true /* isFromSuggestionStrip */);
         } else {
             // If we're not showing the "Touch again to save", then update the suggestion strip.
             // That's going to be predictions (or punctuation suggestions), so INPUT_STYLE_NONE.
@@ -1485,6 +1486,11 @@
         if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return;
         final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>();
         final String typedWord = range.mWord.toString();
+        suggestions.add(new SuggestedWordInfo(typedWord,
+                SuggestedWords.MAX_SUGGESTIONS + 1,
+                SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
+                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
         if (!isResumableWord(settingsValues, typedWord)) {
             mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
             return;
@@ -1517,30 +1523,14 @@
         mConnection.maybeMoveTheCursorAroundAndRestoreToWorkaroundABug();
         mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor,
                 expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor());
-        if (suggestions.size() <= 0) {
+        if (suggestions.size() <= 1) {
             // If there weren't any suggestion spans on this word, suggestions#size() will be 1
             // if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we
             // have no useful suggestions, so we will try to compute some for it instead.
             mInputLogicHandler.getSuggestedWords(Suggest.SESSION_ID_TYPING,
                     SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() {
                         @Override
-                        public void onGetSuggestedWords(
-                                final SuggestedWords suggestedWordsIncludingTypedWord) {
-                            final SuggestedWords suggestedWords;
-                            if (suggestedWordsIncludingTypedWord.size() > 1) {
-                                // We were able to compute new suggestions for this word.
-                                // Remove the typed word, since we don't want to display it in this
-                                // case. The #getSuggestedWordsExcludingTypedWordForRecorrection()
-                                // method sets willAutoCorrect to false.
-                                suggestedWords = suggestedWordsIncludingTypedWord
-                                        .getSuggestedWordsExcludingTypedWordForRecorrection();
-                            } else {
-                                // No saved suggestions, and we were unable to compute any good one
-                                // either. Rather than displaying an empty suggestion strip, we'll
-                                // display the original word alone in the middle.
-                                // Since there is only one word, willAutoCorrect is false.
-                                suggestedWords = suggestedWordsIncludingTypedWord;
-                            }
+                        public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
                             mIsAutoCorrectionIndicatorOn = false;
                             mLatinIME.mHandler.showSuggestionStrip(suggestedWords);
                         }});
@@ -1684,7 +1674,8 @@
                         mConnection.getExpectedSelectionStart(),
                         mConnection.getExpectedSelectionEnd());
             }
-            mSuggestionStripViewAccessor.showAddToDictionaryHint(originallyTypedWordString);
+            mSuggestionStripViewAccessor.suggestAddingToDictionary(originallyTypedWordString,
+                    false /* isFromSuggestionStrip */);
         } else {
             // We have a separator between the word and the cursor: we should show predictions.
             inputTransaction.setRequiresUpdateSuggestions();
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index d559399..0c84414 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -553,12 +553,12 @@
         return countInStrip;
     }
 
-    public void layoutAddToDictionaryHint(final String word, final ViewGroup addToDictionaryStrip) {
-        final boolean shouldShowUiToAcceptTypedWord = Settings.getInstance().getCurrent()
-                .mShouldShowLxxSuggestionUi;
+    public void layoutAddToDictionaryHint(final String word, final ViewGroup addToDictionaryStrip,
+            final boolean shouldShowWordToSave) {
+        final boolean showsHintWithWord = shouldShowWordToSave
+                || !Settings.getInstance().getCurrent().mShouldShowLxxSuggestionUi;
         final int stripWidth = addToDictionaryStrip.getWidth();
-        final int width = shouldShowUiToAcceptTypedWord ? stripWidth
-                : stripWidth - mDividerWidth - mPadding * 2;
+        final int width = stripWidth - (showsHintWithWord ? mDividerWidth + mPadding * 2 : 0);
 
         final TextView wordView = (TextView)addToDictionaryStrip.findViewById(R.id.word_to_save);
         wordView.setTextColor(mColorTypedWord);
@@ -569,7 +569,7 @@
         wordView.setText(wordToSave);
         wordView.setTextScaleX(wordScaleX);
         setLayoutWeight(wordView, mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT);
-        final int wordVisibility = shouldShowUiToAcceptTypedWord ? View.GONE : View.VISIBLE;
+        final int wordVisibility = showsHintWithWord ? View.VISIBLE : View.GONE;
         wordView.setVisibility(wordVisibility);
         addToDictionaryStrip.findViewById(R.id.word_to_save_divider).setVisibility(wordVisibility);
 
@@ -579,12 +579,7 @@
         final float hintWeight;
         final TextView hintView = (TextView)addToDictionaryStrip.findViewById(
                 R.id.hint_add_to_dictionary);
-        if (shouldShowUiToAcceptTypedWord) {
-            hintText = res.getText(R.string.hint_add_to_dictionary_without_word);
-            hintWidth = width;
-            hintWeight = 1.0f;
-            hintView.setGravity(Gravity.CENTER);
-        } else {
+        if (showsHintWithWord) {
             final boolean isRtlLanguage = (ViewCompat.getLayoutDirection(addToDictionaryStrip)
                     == ViewCompat.LAYOUT_DIRECTION_RTL);
             final String arrow = isRtlLanguage ? RIGHTWARDS_ARROW : LEFTWARDS_ARROW;
@@ -595,6 +590,11 @@
             hintWidth = width - wordWidth;
             hintWeight = 1.0f - mCenterSuggestionWeight;
             hintView.setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
+        } else {
+            hintText = res.getText(R.string.hint_add_to_dictionary_without_word);
+            hintWidth = width;
+            hintWeight = 1.0f;
+            hintView.setGravity(Gravity.CENTER);
         }
         hintView.setTextColor(mColorAutoCorrect);
         final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint());
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index e40fd88..789d549 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -231,8 +231,8 @@
         return mStripVisibilityGroup.isShowingAddToDictionaryStrip();
     }
 
-    public void showAddToDictionaryHint(final String word) {
-        mLayoutHelper.layoutAddToDictionaryHint(word, mAddToDictionaryStrip);
+    public void showAddToDictionaryHint(final String word, final boolean shouldShowWordToSave) {
+        mLayoutHelper.layoutAddToDictionaryHint(word, mAddToDictionaryStrip, shouldShowWordToSave);
         // {@link TextView#setTag()} is used to hold the word to be added to dictionary. The word
         // will be extracted at {@link #onClick(View)}.
         mAddToDictionaryStrip.setTag(word);
@@ -501,7 +501,7 @@
             return;
         }
         final Object tag = view.getTag();
-        // {@link String} tag is set at {@link #showAddToDictionaryHint(String,CharSequence)}.
+        // {@link String} tag is set at {@link #suggestAddingToDictionary(String,CharSequence)}.
         if (tag instanceof String) {
             final String wordToSave = (String)tag;
             mListener.addWordToUserDictionary(wordToSave);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
index 5270845..5c86a02 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
@@ -22,7 +22,7 @@
  * An object that gives basic control of a suggestion strip and some info on it.
  */
 public interface SuggestionStripViewAccessor {
-    public void showAddToDictionaryHint(final String word);
+    public void suggestAddingToDictionary(final String word, final boolean isFromSuggestionStrip);
     public boolean isShowingAddToDictionaryHint();
     public void dismissAddToDictionaryHint();
     public void setNeutralSuggestionStrip();
diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
index 563261f..221541e 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
@@ -59,40 +59,6 @@
                 SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */);
     }
 
-    public void testGetSuggestedWordsExcludingTypedWord() {
-        final String TYPED_WORD = "typed";
-        final int NUMBER_OF_ADDED_SUGGESTIONS = 5;
-        final int KIND_OF_SECOND_CORRECTION = SuggestedWordInfo.KIND_CORRECTION;
-        final ArrayList<SuggestedWordInfo> list = new ArrayList<>();
-        list.add(createTypedWordInfo(TYPED_WORD));
-        for (int i = 0; i < NUMBER_OF_ADDED_SUGGESTIONS; ++i) {
-            list.add(createCorrectionWordInfo(Integer.toString(i)));
-        }
-
-        final SuggestedWords words = new SuggestedWords(
-                list, null /* rawSuggestions */,
-                false /* typedWordValid */,
-                false /* willAutoCorrect */,
-                false /* isObsoleteSuggestions */,
-                SuggestedWords.INPUT_STYLE_NONE);
-        assertEquals(NUMBER_OF_ADDED_SUGGESTIONS + 1, words.size());
-        assertEquals("typed", words.getWord(0));
-        assertTrue(words.getInfo(0).isKindOf(SuggestedWordInfo.KIND_TYPED));
-        assertEquals("0", words.getWord(1));
-        assertTrue(words.getInfo(1).isKindOf(KIND_OF_SECOND_CORRECTION));
-        assertEquals("4", words.getWord(5));
-        assertTrue(words.getInfo(5).isKindOf(KIND_OF_SECOND_CORRECTION));
-
-        final SuggestedWords wordsWithoutTyped =
-                words.getSuggestedWordsExcludingTypedWordForRecorrection();
-        // Make sure that the typed word has indeed been excluded, by testing the size of the
-        // suggested words, the string and the kind of the top suggestion, which should match
-        // the string and kind of what we inserted after the typed word.
-        assertEquals(words.size() - 1, wordsWithoutTyped.size());
-        assertEquals("0", wordsWithoutTyped.getWord(0));
-        assertTrue(wordsWithoutTyped.getInfo(0).isKindOf(KIND_OF_SECOND_CORRECTION));
-    }
-
     // Helper for testGetTransformedWordInfo
     private SuggestedWordInfo transformWordInfo(final String info,
             final int trailingSingleQuotesCount) {
@@ -141,9 +107,14 @@
         assertNotNull(typedWord);
         assertEquals(TYPED_WORD, typedWord.mWord);
 
-        // Make sure getTypedWordInfoOrNull() returns null.
-        final SuggestedWords wordsWithoutTypedWord =
-                wordsWithTypedWord.getSuggestedWordsExcludingTypedWordForRecorrection();
+        // Make sure getTypedWordInfoOrNull() returns null when no typed word.
+        list.remove(0);
+        final SuggestedWords wordsWithoutTypedWord = new SuggestedWords(
+                list, null /* rawSuggestions */,
+                false /* typedWordValid */,
+                false /* willAutoCorrect */,
+                false /* isObsoleteSuggestions */,
+                SuggestedWords.INPUT_STYLE_NONE);
         assertNull(wordsWithoutTypedWord.getTypedWordInfoOrNull());
 
         // Make sure getTypedWordInfoOrNull() returns null.