Merge "Remove suggestion_word and suggestion_info layout files"
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index 36ebbb5..672759a 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -487,24 +487,20 @@
         deleteKey.setTag(Constants.CODE_DELETE);
         deleteKey.setOnTouchListener(mDeleteKeyOnTouchListener);
 
-        // alphabetKey depends only on OnTouchListener as it does everything in key-press in
-        // ACTION_DOWN.
+        // alphabetKey, alphabetKey2, and spaceKey depend on {@link View.OnClickListener} as well as
+        // {@link View.OnTouchListener}. {@link View.OnTouchListener} is used as the trigger of
+        // key-press, while {@link View.OnClickListener} is used as the trigger of key-release which
+        // does not occur if the event is canceled by moving off the finger from the view.
         final ImageView alphabetKey = (ImageView)findViewById(R.id.emoji_keyboard_alphabet);
         alphabetKey.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
-        alphabetKey.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL);
+        alphabetKey.setTag(Constants.CODE_ALPHA_FROM_EMOJI);
         alphabetKey.setOnTouchListener(this);
-
-        // alphabetKey2 depends only on OnTouchListener as it does everything in key-press in
-        // ACTION_DOWN.
+        alphabetKey.setOnClickListener(this);
         final ImageView alphabetKey2 = (ImageView)findViewById(R.id.emoji_keyboard_alphabet2);
         alphabetKey2.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
-        alphabetKey2.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL);
+        alphabetKey2.setTag(Constants.CODE_ALPHA_FROM_EMOJI);
         alphabetKey2.setOnTouchListener(this);
-
-        // spaceKey depends on {@link View.OnClickListener} as well as {@link View.OnTouchListener}.
-        // {@link View.OnTouchListener} is used as the trigger of key-press while
-        // {@link View.OnClickListener} is used as the trigger of key-release which may not occur
-        // if the event is canceled by moving off the finger from the view.
+        alphabetKey2.setOnClickListener(this);
         final ImageView spaceKey = (ImageView)findViewById(R.id.emoji_keyboard_space);
         spaceKey.setBackgroundResource(mKeyBackgroundId);
         spaceKey.setTag(Constants.CODE_SPACE);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
index dc815e5..78809d5 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
@@ -54,6 +54,7 @@
         "key_shift_enter",
         "key_language_switch",
         "key_emoji",
+        "key_alpha_from_emoji",
         "key_unspecified",
         "key_left_parenthesis",
         "key_right_parenthesis",
@@ -91,6 +92,7 @@
         Constants.CODE_SHIFT_ENTER,
         Constants.CODE_LANGUAGE_SWITCH,
         Constants.CODE_EMOJI,
+        Constants.CODE_ALPHA_FROM_EMOJI,
         Constants.CODE_UNSPECIFIED,
         CODE_LEFT_PARENTHESIS,
         CODE_RIGHT_PARENTHESIS,
@@ -119,6 +121,7 @@
         DEFAULT[13],
         DEFAULT[14],
         DEFAULT[15],
+        DEFAULT[16],
         CODE_RIGHT_PARENTHESIS,
         CODE_LEFT_PARENTHESIS,
         CODE_GREATER_THAN_SIGN,
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index 0c80ce2..ec0b5c9 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -645,6 +645,8 @@
             updateAlphabetShiftState(autoCaps, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE);
         } else if (code == Constants.CODE_EMOJI) {
             setEmojiKeyboard();
+        } else if (code == Constants.CODE_ALPHA_FROM_EMOJI) {
+            setAlphabetKeyboard();
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index 0477133..0b396b1 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -217,8 +217,9 @@
     public static final int CODE_EMOJI = -11;
     public static final int CODE_SHIFT_ENTER = -12;
     public static final int CODE_SYMBOL_SHIFT = -13;
+    public static final int CODE_ALPHA_FROM_EMOJI = -14;
     // Code value representing the code is not specified.
-    public static final int CODE_UNSPECIFIED = -14;
+    public static final int CODE_UNSPECIFIED = -15;
 
     public static boolean isLetterCode(final int code) {
         return code >= CODE_SPACE;
@@ -241,6 +242,7 @@
         case CODE_UNSPECIFIED: return "unspec";
         case CODE_TAB: return "tab";
         case CODE_ENTER: return "enter";
+        case CODE_ALPHA_FROM_EMOJI: return "alpha";
         default:
             if (code < CODE_SPACE) return String.format("'\\u%02x'", code);
             if (code < 0x100) return String.format("'%c'", code);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 944fa73..e8ea5e3 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -193,9 +193,12 @@
                     if (msg.arg2 == ARG2_WITH_TYPED_WORD) {
                         final Pair<SuggestedWords, String> p =
                                 (Pair<SuggestedWords, String>) msg.obj;
-                        latinIme.showSuggestionStripWithTypedWord(p.first, p.second);
+                        // [IL]: this is the only place where the second arg is not
+                        // suggestedWords.mTypedWord.
+                        latinIme.showSuggestionStrip(p.first, p.second);
                     } else {
-                        latinIme.showSuggestionStrip((SuggestedWords) msg.obj);
+                        final SuggestedWords suggestedWords = (SuggestedWords) msg.obj;
+                        latinIme.showSuggestionStrip(suggestedWords, suggestedWords.mTypedWord);
                     }
                 } else {
                     latinIme.showGesturePreviewAndSuggestionStrip((SuggestedWords) msg.obj,
@@ -1020,17 +1023,16 @@
     private void setSuggestionStripShownInternal(final boolean shown,
             final boolean needsInputViewShown) {
         // TODO: Modify this if we support suggestions with hard keyboard
-        if (onEvaluateInputViewShown() && mSuggestionStripView != null) {
-            final boolean inputViewShown = mKeyboardSwitcher.isShowingMainKeyboardOrEmojiPalettes();
-            final boolean shouldShowSuggestions = shown
-                    && (needsInputViewShown ? inputViewShown : true);
-            if (isFullscreenMode()) {
-                mSuggestionStripView.setVisibility(
-                        shouldShowSuggestions ? View.VISIBLE : View.GONE);
-            } else {
-                mSuggestionStripView.setVisibility(
-                        shouldShowSuggestions ? View.VISIBLE : View.INVISIBLE);
-            }
+        if (!onEvaluateInputViewShown() || null == mSuggestionStripView) {
+            return;
+        }
+        final boolean inputViewShown = mKeyboardSwitcher.isShowingMainKeyboardOrEmojiPalettes();
+        final boolean shouldShowSuggestions = shown
+                && (needsInputViewShown ? inputViewShown : true);
+        if (shouldShowSuggestions) {
+            mSuggestionStripView.setVisibility(View.VISIBLE);
+        } else {
+            mSuggestionStripView.setVisibility(isFullscreenMode() ? View.GONE : View.INVISIBLE);
         }
     }
 
@@ -1271,7 +1273,7 @@
     // This method must run on the UI Thread.
     private void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
             final boolean dismissGestureFloatingPreviewText) {
-        showSuggestionStrip(suggestedWords);
+        showSuggestionStrip(suggestedWords, suggestedWords.mTypedWord);
         final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
         mainKeyboardView.showGestureFloatingPreviewText(suggestedWords);
         if (dismissGestureFloatingPreviewText) {
@@ -1325,27 +1327,14 @@
     }
 
     // TODO[IL]: Define a clear interface for this
-    public void setSuggestedWords(final SuggestedWords words, final boolean shouldShow) {
+    public void setSuggestedWords(final SuggestedWords suggestedWords, final boolean shouldShow) {
+        mInputLogic.setSuggestedWords(suggestedWords);
         if (mSuggestionStripView != null) {
-            mSuggestionStripView.setSuggestions(
-                    words, SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype()));
-            mKeyboardSwitcher.onAutoCorrectionStateChanged(words.mWillAutoCorrect);
+            mSuggestionStripView.setSuggestions(suggestedWords,
+                    SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype()));
+            mKeyboardSwitcher.onAutoCorrectionStateChanged(suggestedWords.mWillAutoCorrect);
+            setSuggestionStripShownInternal(shouldShow, true /* needsInputViewShown */);
         }
-        mInputLogic.mSuggestedWords = words;
-        final boolean newAutoCorrectionIndicator = words.mWillAutoCorrect;
-        // Put a blue underline to a word in TextView which will be auto-corrected.
-        if (mInputLogic.mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator
-                && mInputLogic.mWordComposer.isComposingWord()) {
-            mInputLogic.mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator;
-            final CharSequence textWithUnderline =
-                    mInputLogic.getTextWithUnderline(mInputLogic.mWordComposer.getTypedWord());
-            // TODO: when called from an updateSuggestionStrip() call that results from a posted
-            // message, this is called outside any batch edit. Potentially, this may result in some
-            // janky flickering of the screen, although the display speed makes it unlikely in
-            // the practice.
-            mInputLogic.mConnection.setComposingText(textWithUnderline, 1);
-        }
-        setSuggestionStripShownInternal(shouldShow, true /* needsInputViewShown */);
     }
 
     // TODO[IL]: Move this out of LatinIME.
@@ -1419,7 +1408,8 @@
         }
     }
 
-    private void showSuggestionStripWithTypedWord(final SuggestedWords sourceSuggestedWords,
+    // TODO[IL]: Define a clean interface for this
+    public void showSuggestionStrip(final SuggestedWords sourceSuggestedWords,
             final String typedWord) {
         final SuggestedWords suggestedWords =
                 sourceSuggestedWords.isEmpty() ? SuggestedWords.EMPTY : sourceSuggestedWords;
@@ -1440,12 +1430,6 @@
         AccessibilityUtils.getInstance().setAutoCorrection(suggestedWords, typedWord);
     }
 
-    // TODO[IL]: Define a clean interface for this
-    public void showSuggestionStrip(final SuggestedWords suggestedWords) {
-        showSuggestionStripWithTypedWord(suggestedWords, suggestedWords.isEmpty() ? null
-                : suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD));
-    }
-
     // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
     // interface
     @Override
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index bb34b7b..982a97a 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -39,6 +39,7 @@
     public static final SuggestedWords EMPTY = new SuggestedWords(
             EMPTY_WORD_INFO_LIST, false, false, false, false, false);
 
+    public final String mTypedWord;
     public final boolean mTypedWordValid;
     // Note: this INCLUDES cases where the word will auto-correct to itself. A good definition
     // of what this flag means would be "the top suggestion is strong enough to auto-correct",
@@ -74,6 +75,7 @@
         mIsObsoleteSuggestions = isObsoleteSuggestions;
         mIsPrediction = isPrediction;
         mSequenceNumber = sequenceNumber;
+        mTypedWord = suggestedWordInfoList.isEmpty() ? null : getWord(INDEX_OF_TYPED_WORD);
     }
 
     public boolean isEmpty() {
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 43d7533..ce3ef53 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -349,6 +349,10 @@
             didAutoCorrect = handleNonSpecialCharacter(settingsValues, Constants.CODE_ENTER,
                     x, y, spaceState, keyboardSwitcher, handler);
             break;
+        case Constants.CODE_ALPHA_FROM_EMOJI:
+            // Note: Switching back from Emoji keyboard to the main keyboard is being handled in
+            // {@link KeyboardState#onCodeInput(int,int)}.
+            break;
         default:
             didAutoCorrect = handleNonSpecialCharacter(settingsValues,
                     code, x, y, spaceState, keyboardSwitcher, handler);
@@ -478,6 +482,25 @@
                 SuggestedWords.EMPTY, true /* dismissGestureFloatingPreviewText */);
     }
 
+    // TODO: on the long term, this method should become private, but it will be difficult.
+    // Especially, how do we deal with InputMethodService.onDisplayCompletions?
+    public void setSuggestedWords(final SuggestedWords suggestedWords) {
+        mSuggestedWords = suggestedWords;
+        final boolean newAutoCorrectionIndicator = suggestedWords.mWillAutoCorrect;
+        // Put a blue underline to a word in TextView which will be auto-corrected.
+        if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator
+                && mWordComposer.isComposingWord()) {
+            mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator;
+            final CharSequence textWithUnderline =
+                    getTextWithUnderline(mWordComposer.getTypedWord());
+            // TODO: when called from an updateSuggestionStrip() call that results from a posted
+            // message, this is called outside any batch edit. Potentially, this may result in some
+            // janky flickering of the screen, although the display speed makes it unlikely in
+            // the practice.
+            mConnection.setComposingText(textWithUnderline, 1);
+        }
+    }
+
     /**
      * Handle inputting a code point to the editor.
      *
@@ -1097,7 +1120,7 @@
         final SuggestedWords suggestedWords = holder.get(null,
                 Constants.GET_SUGGESTED_WORDS_TIMEOUT);
         if (suggestedWords != null) {
-            mLatinIME.showSuggestionStrip(suggestedWords);
+            mLatinIME.showSuggestionStrip(suggestedWords, suggestedWords.mTypedWord);
         }
     }
 
@@ -1599,8 +1622,10 @@
             final int indexOfLastSpace = batchInputText.lastIndexOf(Constants.CODE_SPACE) + 1;
             if (0 != indexOfLastSpace) {
                 mConnection.commitText(batchInputText.substring(0, indexOfLastSpace), 1);
-                mLatinIME.showSuggestionStrip(
-                        suggestedWords.getSuggestedWordsForLastWordOfPhraseGesture());
+                final SuggestedWords suggestedWordsForLastWordOfPhraseGesture =
+                        suggestedWords.getSuggestedWordsForLastWordOfPhraseGesture();
+                mLatinIME.showSuggestionStrip(suggestedWordsForLastWordOfPhraseGesture,
+                        suggestedWordsForLastWordOfPhraseGesture.mTypedWord);
             }
             final String lastWord = batchInputText.substring(indexOfLastSpace);
             mWordComposer.setBatchInputWord(lastWord);
diff --git a/native/jni/src/suggest/core/dictionary/suggestions_output_utils.cpp b/native/jni/src/suggest/core/dictionary/suggestions_output_utils.cpp
index d219757..b810637 100644
--- a/native/jni/src/suggest/core/dictionary/suggestions_output_utils.cpp
+++ b/native/jni/src/suggest/core/dictionary/suggestions_output_utils.cpp
@@ -58,12 +58,6 @@
         ++outputWordIndex;
     }
 
-    // Initial value of the loop index for terminal nodes (words)
-    int doubleLetterTerminalIndex = -1;
-    DoubleLetterLevel doubleLetterLevel = NOT_A_DOUBLE_LETTER;
-    scoringPolicy->searchWordWithDoubleLetter(terminals, terminalSize,
-            &doubleLetterTerminalIndex, &doubleLetterLevel);
-
     int maxScore = S_INT_MIN;
     // Force autocorrection for obvious long multi-word suggestions when the top suggestion is
     // a long multiple words suggestion.
@@ -92,8 +86,8 @@
         if (DEBUG_GEO_FULL) {
             terminalDicNode->dump("OUT:");
         }
-        const float doubleLetterCost = scoringPolicy->getDoubleLetterDemotionDistanceCost(
-                terminalIndex, doubleLetterTerminalIndex, doubleLetterLevel);
+        const float doubleLetterCost =
+                scoringPolicy->getDoubleLetterDemotionDistanceCost(terminalDicNode);
         const float compoundDistance = terminalDicNode->getCompoundDistance(languageWeight)
                 + doubleLetterCost;
         const bool isPossiblyOffensiveWord =
diff --git a/native/jni/src/suggest/core/policy/scoring.h b/native/jni/src/suggest/core/policy/scoring.h
index 5ae3d21..7833834 100644
--- a/native/jni/src/suggest/core/policy/scoring.h
+++ b/native/jni/src/suggest/core/policy/scoring.h
@@ -34,14 +34,10 @@
             int *const type, int *const freq) const = 0;
     virtual void safetyNetForMostProbableString(const int terminalSize,
             const int maxScore, int *const outputCodePoints, int *const frequencies) const = 0;
-    // TODO: Make more generic
-    virtual void searchWordWithDoubleLetter(DicNode *terminals, const int terminalSize,
-            int *doubleLetterTerminalIndex, DoubleLetterLevel *doubleLetterLevel) const = 0;
     virtual float getAdjustedLanguageWeight(DicTraverseSession *const traverseSession,
             DicNode *const terminals, const int size) const = 0;
-    virtual float getDoubleLetterDemotionDistanceCost(const int terminalIndex,
-            const int doubleLetterTerminalIndex,
-            const DoubleLetterLevel doubleLetterLevel) const = 0;
+    virtual float getDoubleLetterDemotionDistanceCost(
+            const DicNode *const terminalDicNode) const = 0;
     virtual bool doesAutoCorrectValidWord() const = 0;
     virtual bool autoCorrectsToMultiWordSuggestionIfTop() const = 0;
     virtual bool sameAsTyped(const DicTraverseSession *const traverseSession,
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
index 186e3ba..c777e72 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
@@ -43,11 +43,6 @@
             const int maxScore, int *const outputCodePoints, int *const frequencies) const {
     }
 
-    AK_FORCE_INLINE void searchWordWithDoubleLetter(DicNode *terminals,
-            const int terminalSize, int *doubleLetterTerminalIndex,
-            DoubleLetterLevel *doubleLetterLevel) const {
-    }
-
     AK_FORCE_INLINE float getAdjustedLanguageWeight(DicTraverseSession *const traverseSession,
              DicNode *const terminals, const int size) const {
         return 1.0f;
@@ -77,9 +72,8 @@
         return static_cast<int>(score * SUGGEST_INTERFACE_OUTPUT_SCALE);
     }
 
-    AK_FORCE_INLINE float getDoubleLetterDemotionDistanceCost(const int terminalIndex,
-            const int doubleLetterTerminalIndex,
-            const DoubleLetterLevel doubleLetterLevel) const {
+    AK_FORCE_INLINE float getDoubleLetterDemotionDistanceCost(
+            const DicNode *const terminalDicNode) const {
         return 0.0f;
     }