Merge "Call setLayerType(LAYER_TYPE_HARDWARE) for EmojiPalettesView too."
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 0aa34e8..323d0c8 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -249,8 +249,18 @@
         final boolean isGesture = composer.isBatchMode();
         if (composerSize <= 1 || !isGesture) {
             if (composerSize > MAX_WORD_LENGTH - 1) return null;
-            for (int i = 0; i < composerSize; i++) {
-                mInputCodePoints[i] = composer.getCodeAt(i);
+            final CharSequence typedWord = composer.getTypedWord();
+            final int charCount = typedWord.length();
+            int typedWordCharIndex = 0;
+            int inputCodePointIndex = 0;
+            while (typedWordCharIndex < charCount) {
+                final int codePoint = Character.codePointAt(typedWord, typedWordCharIndex);
+                mInputCodePoints[inputCodePointIndex] = codePoint;
+                typedWordCharIndex += Character.charCount(codePoint);
+                inputCodePointIndex += 1;
+                if (inputCodePointIndex >= MAX_WORD_LENGTH) {
+                    break;
+                }
             }
         }
 
diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java
index 2a16ab5..a3b0306 100644
--- a/java/src/com/android/inputmethod/latin/LastComposedWord.java
+++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java
@@ -44,7 +44,6 @@
 
     public static final String NOT_A_SEPARATOR = "";
 
-    public final int[] mPrimaryKeyCodes;
     public final ArrayList<Event> mEvents;
     public final String mTypedWord;
     public final CharSequence mCommittedWord;
@@ -57,16 +56,15 @@
     private boolean mActive;
 
     public static final LastComposedWord NOT_A_COMPOSED_WORD =
-            new LastComposedWord(null, new ArrayList<Event>(), null, "", "",
-            NOT_A_SEPARATOR, null, WordComposer.CAPS_MODE_OFF);
+            new LastComposedWord(new ArrayList<Event>(), null, "", "",
+                    NOT_A_SEPARATOR, null, WordComposer.CAPS_MODE_OFF);
 
     // Warning: this is using the passed objects as is and fully expects them to be
     // immutable. Do not fiddle with their contents after you passed them to this constructor.
-    public LastComposedWord(final int[] primaryKeyCodes, final ArrayList<Event> events,
+    public LastComposedWord(final ArrayList<Event> events,
             final InputPointers inputPointers, final String typedWord,
             final CharSequence committedWord, final String separatorString,
             final String prevWord, final int capitalizedMode) {
-        mPrimaryKeyCodes = primaryKeyCodes;
         if (inputPointers != null) {
             mInputPointers.copy(inputPointers);
         }
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index a955f37..4638c8a 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -43,11 +43,6 @@
 
     private CombinerChain mCombinerChain;
 
-    // An array of code points representing the characters typed so far.
-    // The array is limited to MAX_WORD_LENGTH code points, but mTypedWord extends past that
-    // and mCodePointSize can go past that. If mCodePointSize is greater than MAX_WORD_LENGTH,
-    // this just does not contain the associated code points past MAX_WORD_LENGTH.
-    private int[] mPrimaryKeyCodes;
     // The list of events that served to compose this string.
     private final ArrayList<Event> mEvents;
     private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH);
@@ -91,7 +86,6 @@
 
     public WordComposer() {
         mCombinerChain = new CombinerChain();
-        mPrimaryKeyCodes = new int[MAX_WORD_LENGTH];
         mEvents = CollectionUtils.newArrayList();
         mTypedWord = new StringBuilder(MAX_WORD_LENGTH);
         mAutoCorrection = null;
@@ -106,7 +100,6 @@
 
     public WordComposer(final WordComposer source) {
         mCombinerChain = source.mCombinerChain;
-        mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length);
         mEvents = new ArrayList<Event>(source.mEvents);
         mTypedWord = new StringBuilder(source.mTypedWord);
         mInputPointers.copy(source.mInputPointers);
@@ -159,14 +152,6 @@
         return size() > 0;
     }
 
-    // TODO: make sure that the index should not exceed MAX_WORD_LENGTH
-    public int getCodeAt(int index) {
-        if (index >= MAX_WORD_LENGTH) {
-            return -1;
-        }
-        return mPrimaryKeyCodes[index];
-    }
-
     public InputPointers getInputPointers() {
         return mInputPointers;
     }
@@ -195,8 +180,6 @@
         refreshSize();
         mCursorPositionWithinWord = mCodePointSize;
         if (newIndex < MAX_WORD_LENGTH) {
-            mPrimaryKeyCodes[newIndex] = primaryCode >= Constants.CODE_SPACE
-                    ? Character.toLowerCase(primaryCode) : primaryCode;
             // In the batch input mode, the {@code mInputPointers} holds batch input points and
             // shouldn't be overridden by the "typed key" coordinates
             // (See {@link #setBatchInputWord}).
@@ -244,15 +227,7 @@
         mCombinerChain.reset();
         int actualMoveAmountWithinWord = 0;
         int cursorPos = mCursorPositionWithinWord;
-        final int[] codePoints;
-        if (mCodePointSize >= MAX_WORD_LENGTH) {
-            // If we have more than MAX_WORD_LENGTH characters, we don't have everything inside
-            // mPrimaryKeyCodes. This should be rare enough that we can afford to just compute
-            // the array on the fly when this happens.
-            codePoints = StringUtils.toCodePointArray(mTypedWord.toString());
-        } else {
-            codePoints = mPrimaryKeyCodes;
-        }
+        final int[] codePoints = StringUtils.toCodePointArray(mTypedWord.toString());
         if (expectedMoveAmount >= 0) {
             // Moving the cursor forward for the expected amount or until the end of the word has
             // been reached, whichever comes first.
@@ -451,9 +426,7 @@
         // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK
         // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
         // the last composed word to ensure this does not happen.
-        final int[] primaryKeyCodes = mPrimaryKeyCodes;
-        mPrimaryKeyCodes = new int[MAX_WORD_LENGTH];
-        final LastComposedWord lastComposedWord = new LastComposedWord(primaryKeyCodes, mEvents,
+        final LastComposedWord lastComposedWord = new LastComposedWord(mEvents,
                 mInputPointers, mTypedWord.toString(), committedWord, separatorString,
                 prevWord, mCapitalizedMode);
         mInputPointers.reset();
@@ -489,7 +462,6 @@
 
     public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord,
             final String previousWord) {
-        mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes;
         mEvents.clear();
         Collections.copy(mEvents, lastComposedWord.mEvents);
         mInputPointers.set(lastComposedWord.mInputPointers);
diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h
index 865aab6..3118cdf 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node.h
@@ -218,10 +218,6 @@
         return CharUtils::isAsciiUpper(c);
     }
 
-    bool isFirstWord() const {
-        return mDicNodeProperties.getPrevWordTerminalPtNodePos() == NOT_A_DICT_POS;
-    }
-
     bool isCompletion(const int inputSize) const {
         return mDicNodeState.mDicNodeStateInput.getInputIndex(0) >= inputSize;
     }
@@ -292,7 +288,9 @@
     // the one that corresponds to the last word of the suggestion, and all the previous words
     // are concatenated together in mDicNodeStateOutput.
     int getTotalNodeSpaceCount() const {
-        if (isFirstWord()) return 0;
+        if (!hasMultipleWords()) {
+            return 0;
+        }
         return CharUtils::getSpaceCount(mDicNodeState.mDicNodeStateOutput.getCodePointBuf(),
                 mDicNodeState.mDicNodeStateOutput.getPrevWordsLength());
     }