Merge "Add/Get n-gram probability entry in languageModelDictContent" into lmp-dev
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 003b011..9a22273 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -52,7 +52,9 @@
         <attr name="spacebarIconWidthRatio" format="float" />
         <!-- Right padding of hint letter to the edge of the key.-->
         <attr name="keyHintLetterPadding" format="dimension" />
-        <!-- Bottom padding of popup hint letter "..." to the edge of the key.-->
+        <!-- Popup hint letter string-->
+        <attr name="keyPopupHintLetter" format="string" />
+        <!-- Bottom padding of popup hint letter to the edge of the key.-->
         <attr name="keyPopupHintLetterPadding" format="dimension" />
         <!-- Right padding of shifted letter hint to the edge of the key.-->
         <attr name="keyShiftedLetterHintPadding" format="dimension" />
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index 6118ce1..a6f390c 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -60,6 +60,8 @@
         <item name="keyPreviewTextColor">@color/key_text_color_holo</item>
         <!-- A negative value to disable key text shadow layer. -->
         <item name="keyTextShadowRadius">-1.0</item>
+        <!-- U+2026: "…" HORIZONTAL ELLIPSIS -->
+        <item name="keyPopupHintLetter">&#x2026;</item>
     </style>
     <style
         name="MainKeyboardView.ICS"
diff --git a/java/res/values/themes-klp.xml b/java/res/values/themes-klp.xml
index 1933860..8782a76 100644
--- a/java/res/values/themes-klp.xml
+++ b/java/res/values/themes-klp.xml
@@ -60,6 +60,8 @@
         <item name="keyPreviewTextColor">@color/key_text_color_holo</item>
         <!-- A negative value to disable key text shadow layer. -->
         <item name="keyTextShadowRadius">-1.0</item>
+        <!-- U+2026: "…" HORIZONTAL ELLIPSIS -->
+        <item name="keyPopupHintLetter">&#x2026;</item>
     </style>
     <style
         name="MainKeyboardView.KLP"
diff --git a/java/res/values/themes-lxx-dark.xml b/java/res/values/themes-lxx-dark.xml
index 0a13158..fa6aa62 100644
--- a/java/res/values/themes-lxx-dark.xml
+++ b/java/res/values/themes-lxx-dark.xml
@@ -61,6 +61,7 @@
         <item name="keyPreviewTextColor">@color/key_text_color_lxx_dark</item>
         <!-- A negative value to disable key text shadow layer. -->
         <item name="keyTextShadowRadius">-1.0</item>
+        <item name="keyPopupHintLetter"></item> <!-- No popup hint letter -->
     </style>
     <style
         name="MainKeyboardView.LXX_Dark"
diff --git a/java/res/values/themes-lxx-light.xml b/java/res/values/themes-lxx-light.xml
index eccecdd..e7350f9 100644
--- a/java/res/values/themes-lxx-light.xml
+++ b/java/res/values/themes-lxx-light.xml
@@ -61,6 +61,7 @@
         <item name="keyPreviewTextColor">@color/key_text_color_lxx_light</item>
         <!-- A negative value to disable key text shadow layer. -->
         <item name="keyTextShadowRadius">-1.0</item>
+        <item name="keyPopupHintLetter"></item> <!-- No popup hint letter -->
     </style>
     <style
         name="MainKeyboardView.LXX_Light"
diff --git a/java/res/xml/key_styles_enter.xml b/java/res/xml/key_styles_enter.xml
index 50530e1..770bf38 100644
--- a/java/res/xml/key_styles_enter.xml
+++ b/java/res/xml/key_styles_enter.xml
@@ -255,6 +255,7 @@
     <!-- Enter key style -->
     <key-style
         latin:styleName="defaultEnterKeyStyle"
+        latin:keySpec="!icon/enter_key|!code/key_enter"
         latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="action"
diff --git a/java/res/xml/key_styles_number.xml b/java/res/xml/key_styles_number.xml
index df4448c..f754b99 100644
--- a/java/res/xml/key_styles_number.xml
+++ b/java/res/xml/key_styles_number.xml
@@ -122,12 +122,4 @@
         latin:keySpec="!icon/space_key_for_number_layout|!code/key_space"
         latin:keyActionFlags="enableLongPress"
         latin:parentStyle="numKeyBaseStyle" />
-    <!-- Override defaultEnterKeyStyle in key_styles_enter.xml -->
-    <key-style
-        latin:styleName="defaultEnterKeyStyle"
-        latin:keySpec="!icon/enter_key|!code/key_enter"
-        latin:keyLabelFlags="preserveCase|autoXScale|followKeyLargeLabelRatio"
-        latin:keyActionFlags="noKeyPreview"
-        latin:backgroundType="functional"
-        latin:parentStyle="navigateMoreKeysStyle" />
 </merge>
diff --git a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java
index deb6809..afbe8c8 100644
--- a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java
@@ -31,6 +31,9 @@
     private static final Method METHOD_setPaddingRelative = CompatUtils.getMethod(
             View.class, "setPaddingRelative",
             int.class, int.class, int.class, int.class);
+    // Note that View.setElevation(float) has been introduced in API level 21.
+    private static final Method METHOD_setElevation = CompatUtils.getMethod(
+            View.class, "setElevation", float.class);
 
     private ViewCompatUtils() {
         // This utility class is not publicly instantiable.
@@ -51,4 +54,11 @@
         }
         CompatUtils.invoke(view, null, METHOD_setPaddingRelative, start, top, end, bottom);
     }
+
+    public static void setElevation(final View view, final float elevation) {
+        if (METHOD_setElevation == null) {
+            return;
+        }
+        CompatUtils.invoke(view, null, METHOD_setElevation, elevation);
+    }
 }
diff --git a/java/src/com/android/inputmethod/event/InputTransaction.java b/java/src/com/android/inputmethod/event/InputTransaction.java
index 5bc9111..b18bf56 100644
--- a/java/src/com/android/inputmethod/event/InputTransaction.java
+++ b/java/src/com/android/inputmethod/event/InputTransaction.java
@@ -33,7 +33,7 @@
 
     // Initial conditions
     public final SettingsValues mSettingsValues;
-    public final Event mEvent;
+    private final Event mEvent;
     public final long mTimestamp;
     public final int mSpaceState;
     public final int mShiftState;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index f967f62..5af0be6 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -29,6 +29,7 @@
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.NinePatchDrawable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 
@@ -48,6 +49,7 @@
  * @attr ref R.styleable#KeyboardView_spacebarBackground
  * @attr ref R.styleable#KeyboardView_spacebarIconWidthRatio
  * @attr ref R.styleable#KeyboardView_keyHintLetterPadding
+ * @attr ref R.styleable#KeyboardView_keyPopupHintLetter
  * @attr ref R.styleable#KeyboardView_keyPopupHintLetterPadding
  * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintPadding
  * @attr ref R.styleable#KeyboardView_keyTextShadowRadius
@@ -74,6 +76,7 @@
     // XML attributes
     private final KeyVisualAttributes mKeyVisualAttributes;
     private final float mKeyHintLetterPadding;
+    private final String mKeyPopupHintLetter;
     private final float mKeyPopupHintLetterPadding;
     private final float mKeyShiftedLetterHintPadding;
     private final float mKeyTextShadowRadius;
@@ -85,9 +88,6 @@
     private final Rect mKeyBackgroundPadding = new Rect();
     private static final float KET_TEXT_SHADOW_RADIUS_DISABLED = -1.0f;
 
-    // HORIZONTAL ELLIPSIS "...", character for popup hint.
-    private static final String POPUP_HINT_CHAR = "\u2026";
-
     // The maximum key label width in the proportion to the key width.
     private static final float MAX_LABEL_RATIO = 0.90f;
 
@@ -132,6 +132,8 @@
                 R.styleable.KeyboardView_spacebarIconWidthRatio, 1.0f);
         mKeyHintLetterPadding = keyboardViewAttr.getDimension(
                 R.styleable.KeyboardView_keyHintLetterPadding, 0.0f);
+        mKeyPopupHintLetter = keyboardViewAttr.getString(
+                R.styleable.KeyboardView_keyPopupHintLetter);
         mKeyPopupHintLetterPadding = keyboardViewAttr.getDimension(
                 R.styleable.KeyboardView_keyPopupHintLetterPadding, 0.0f);
         mKeyShiftedLetterHintPadding = keyboardViewAttr.getDimension(
@@ -468,6 +470,9 @@
     // Draw popup hint "..." at the bottom right corner of the key.
     protected void drawKeyPopupHint(final Key key, final Canvas canvas, final Paint paint,
             final KeyDrawParams params) {
+        if (TextUtils.isEmpty(mKeyPopupHintLetter)) {
+            return;
+        }
         final int keyWidth = key.getDrawWidth();
         final int keyHeight = key.getHeight();
 
@@ -478,7 +483,7 @@
         final float hintX = keyWidth - mKeyHintLetterPadding
                 - TypefaceUtils.getReferenceCharWidth(paint) / 2.0f;
         final float hintY = keyHeight - mKeyPopupHintLetterPadding;
-        canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint);
+        canvas.drawText(mKeyPopupHintLetter, hintX, hintY, paint);
     }
 
     protected static void drawIcon(final Canvas canvas, final Drawable icon, final int x,
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 9e20abc..97457b2 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -426,12 +426,17 @@
             cancelDoubleSpacePeriodCountdown();
         }
 
-        if (processedEvent.isConsumed()) {
-            handleConsumedEvent(inputTransaction);
-        } else if (processedEvent.isFunctionalKeyEvent()) {
-            handleFunctionalEvent(inputTransaction, currentKeyboardScriptId, handler);
-        } else {
-            handleNonFunctionalEvent(inputTransaction, handler);
+        Event currentEvent = processedEvent;
+        while (null != currentEvent) {
+            if (currentEvent.isConsumed()) {
+                handleConsumedEvent(currentEvent, inputTransaction);
+            } else if (currentEvent.isFunctionalKeyEvent()) {
+                handleFunctionalEvent(currentEvent, inputTransaction, currentKeyboardScriptId,
+                        handler);
+            } else {
+                handleNonFunctionalEvent(currentEvent, inputTransaction, handler);
+            }
+            currentEvent = currentEvent.mNextEvent;
         }
         if (!inputTransaction.didAutoCorrect() && processedEvent.mKeyCode != Constants.CODE_SHIFT
                 && processedEvent.mKeyCode != Constants.CODE_CAPSLOCK
@@ -584,13 +589,14 @@
      * Consumed events represent events that have already been consumed, typically by the
      * combining chain.
      *
+     * @param event The event to handle.
      * @param inputTransaction The transaction in progress.
      */
-    private void handleConsumedEvent(final InputTransaction inputTransaction) {
+    private void handleConsumedEvent(final Event event, final InputTransaction inputTransaction) {
         // A consumed event may have text to commit and an update to the composing state, so
         // we evaluate both. With some combiners, it's possible than an event contains both
         // and we enter both of the following if clauses.
-        final CharSequence textToCommit = inputTransaction.mEvent.getTextToCommit();
+        final CharSequence textToCommit = event.getTextToCommit();
         if (!TextUtils.isEmpty(textToCommit)) {
             mConnection.commitText(textToCommit, 1);
             inputTransaction.setDidAffectContents();
@@ -611,15 +617,15 @@
      * manage keyboard-related stuff like shift, language switch, settings, layout switch, or
      * any key that results in multiple code points like the ".com" key.
      *
+     * @param event The event to handle.
      * @param inputTransaction The transaction in progress.
      */
-    private void handleFunctionalEvent(final InputTransaction inputTransaction,
+    private void handleFunctionalEvent(final Event event, final InputTransaction inputTransaction,
             // TODO: remove these arguments
             final int currentKeyboardScriptId, final LatinIME.UIHandler handler) {
-        final Event event = inputTransaction.mEvent;
         switch (event.mKeyCode) {
             case Constants.CODE_DELETE:
-                handleBackspace(inputTransaction, currentKeyboardScriptId);
+                handleBackspaceEvent(event, inputTransaction, currentKeyboardScriptId);
                 // Backspace is a functional key, but it affects the contents of the editor.
                 inputTransaction.setDidAffectContents();
                 break;
@@ -670,11 +676,7 @@
                 // TODO: remove this object
                 final Event tmpEvent = Event.createSoftwareKeypressEvent(Constants.CODE_ENTER,
                         event.mKeyCode, event.mX, event.mY, event.isKeyRepeat());
-                final InputTransaction tmpTransaction = new InputTransaction(
-                        inputTransaction.mSettingsValues, tmpEvent,
-                        inputTransaction.mTimestamp, inputTransaction.mSpaceState,
-                        inputTransaction.mShiftState);
-                handleNonSpecialCharacter(tmpTransaction, handler);
+                handleNonSpecialCharacterEvent(tmpEvent, inputTransaction, handler);
                 // Shift + Enter is treated as a functional key but it results in adding a new
                 // line, so that does affect the contents of the editor.
                 inputTransaction.setDidAffectContents();
@@ -690,12 +692,13 @@
      * These events are generally events that cause input, but in some cases they may do other
      * things like trigger an editor action.
      *
+     * @param event The event to handle.
      * @param inputTransaction The transaction in progress.
      */
-    private void handleNonFunctionalEvent(final InputTransaction inputTransaction,
+    private void handleNonFunctionalEvent(final Event event,
+            final InputTransaction inputTransaction,
             // TODO: remove this argument
             final LatinIME.UIHandler handler) {
-        final Event event = inputTransaction.mEvent;
         inputTransaction.setDidAffectContents();
         switch (event.mCodePoint) {
             case Constants.CODE_ENTER:
@@ -718,11 +721,11 @@
                 } else {
                     // No action label, and the action from imeOptions is NONE: this is a regular
                     // enter key that should input a carriage return.
-                    handleNonSpecialCharacter(inputTransaction, handler);
+                    handleNonSpecialCharacterEvent(event, inputTransaction, handler);
                 }
                 break;
             default:
-                handleNonSpecialCharacter(inputTransaction, handler);
+                handleNonSpecialCharacterEvent(event, inputTransaction, handler);
                 break;
         }
     }
@@ -735,16 +738,18 @@
      * manage keyboard-related stuff like shift, language switch, settings, layout switch, or
      * any key that results in multiple code points like the ".com" key.
      *
+     * @param event The event to handle.
      * @param inputTransaction The transaction in progress.
      */
-    private void handleNonSpecialCharacter(final InputTransaction inputTransaction,
+    private void handleNonSpecialCharacterEvent(final Event event,
+            final InputTransaction inputTransaction,
             // TODO: remove this argument
             final LatinIME.UIHandler handler) {
-        final int codePoint = inputTransaction.mEvent.mCodePoint;
+        final int codePoint = event.mCodePoint;
         mSpaceState = SpaceState.NONE;
         if (inputTransaction.mSettingsValues.isWordSeparator(codePoint)
                 || Character.getType(codePoint) == Character.OTHER_SYMBOL) {
-            handleSeparator(inputTransaction, handler);
+            handleSeparatorEvent(event, inputTransaction, handler);
         } else {
             if (SpaceState.PHANTOM == inputTransaction.mSpaceState) {
                 if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
@@ -756,21 +761,23 @@
                     commitTyped(inputTransaction.mSettingsValues, LastComposedWord.NOT_A_SEPARATOR);
                 }
             }
-            handleNonSeparator(inputTransaction.mSettingsValues, inputTransaction);
+            handleNonSeparatorEvent(event, inputTransaction.mSettingsValues, inputTransaction);
         }
     }
 
     /**
      * Handle a non-separator.
+     * @param event The event to handle.
      * @param settingsValues The current settings values.
      * @param inputTransaction The transaction in progress.
      */
-    private void handleNonSeparator(final SettingsValues settingsValues,
+    private void handleNonSeparatorEvent(final Event event, final SettingsValues settingsValues,
             final InputTransaction inputTransaction) {
-        final int codePoint = inputTransaction.mEvent.mCodePoint;
+        final int codePoint = event.mCodePoint;
         // TODO: refactor this method to stop flipping isComposingWord around all the time, and
-        // make it shorter (possibly cut into several pieces). Also factor handleNonSpecialCharacter
-        // which has the same name as other handle* methods but is not the same.
+        // make it shorter (possibly cut into several pieces). Also factor
+        // handleNonSpecialCharacterEvent which has the same name as other handle* methods but is
+        // not the same.
         boolean isComposingWord = mWordComposer.isComposingWord();
 
         // TODO: remove isWordConnector() and use isUsuallyFollowedBySpace() instead.
@@ -817,7 +824,7 @@
             resetComposingState(false /* alsoResetLastComposedWord */);
         }
         if (isComposingWord) {
-            mWordComposer.applyProcessedEvent(inputTransaction.mEvent);
+            mWordComposer.applyProcessedEvent(event);
             // If it's the first letter, make note of auto-caps state
             if (mWordComposer.isSingleLetter()) {
                 mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState);
@@ -825,10 +832,10 @@
             mConnection.setComposingText(getTextWithUnderline(
                     mWordComposer.getTypedWord()), 1);
         } else {
-            final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(
+            final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(event,
                     inputTransaction);
 
-            if (swapWeakSpace && trySwapSwapperAndSpace(inputTransaction)) {
+            if (swapWeakSpace && trySwapSwapperAndSpace(event, inputTransaction)) {
                 mSpaceState = SpaceState.WEAK;
             } else {
                 sendKeyCodePoint(settingsValues, codePoint);
@@ -841,12 +848,13 @@
 
     /**
      * Handle input of a separator code point.
+     * @param event The event to handle.
      * @param inputTransaction The transaction in progress.
      */
-    private void handleSeparator(final InputTransaction inputTransaction,
+    private void handleSeparatorEvent(final Event event, final InputTransaction inputTransaction,
             // TODO: remove this argument
             final LatinIME.UIHandler handler) {
-        final int codePoint = inputTransaction.mEvent.mCodePoint;
+        final int codePoint = event.mCodePoint;
         final SettingsValues settingsValues = inputTransaction.mSettingsValues;
         final boolean wasComposingWord = mWordComposer.isComposingWord();
         // We avoid sending spaces in languages without spaces if we were composing.
@@ -872,7 +880,7 @@
             }
         }
 
-        final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(
+        final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(event,
                 inputTransaction);
 
         final boolean isInsideDoubleQuoteOrAfterDigit = Constants.CODE_DOUBLE_QUOTE == codePoint
@@ -897,10 +905,10 @@
             promotePhantomSpace(settingsValues);
         }
 
-        if (tryPerformDoubleSpacePeriod(inputTransaction)) {
+        if (tryPerformDoubleSpacePeriod(event, inputTransaction)) {
             mSpaceState = SpaceState.DOUBLE;
             inputTransaction.setRequiresUpdateSuggestions();
-        } else if (swapWeakSpace && trySwapSwapperAndSpace(inputTransaction)) {
+        } else if (swapWeakSpace && trySwapSwapperAndSpace(event, inputTransaction)) {
             mSpaceState = SpaceState.SWAP_PUNCTUATION;
             mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
         } else if (Constants.CODE_SPACE == codePoint) {
@@ -947,12 +955,12 @@
 
     /**
      * Handle a press on the backspace key.
+     * @param event The event to handle.
      * @param inputTransaction The transaction in progress.
      */
-    private void handleBackspace(final InputTransaction inputTransaction,
+    private void handleBackspaceEvent(final Event event, final InputTransaction inputTransaction,
             // TODO: remove this argument, put it into settingsValues
             final int currentKeyboardScriptId) {
-        final Event event = inputTransaction.mEvent;
         mSpaceState = SpaceState.NONE;
         mDeleteCount++;
 
@@ -1103,16 +1111,18 @@
      *
      * This method will check that there are two characters before the cursor and that the first
      * one is a space before it does the actual swapping.
+     * @param event The event to handle.
      * @param inputTransaction The transaction in progress.
      * @return true if the swap has been performed, false if it was prevented by preliminary checks.
      */
-    private boolean trySwapSwapperAndSpace(final InputTransaction inputTransaction) {
+    private boolean trySwapSwapperAndSpace(final Event event,
+            final InputTransaction inputTransaction) {
         final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
         if (Constants.CODE_SPACE != codePointBeforeCursor) {
             return false;
         }
         mConnection.deleteSurroundingText(1, 0);
-        final String text = inputTransaction.mEvent.getTextToCommit() + " ";
+        final String text = event.getTextToCommit() + " ";
         mConnection.commitText(text, 1);
         inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
         return true;
@@ -1120,13 +1130,14 @@
 
     /*
      * Strip a trailing space if necessary and returns whether it's a swap weak space situation.
+     * @param event The event to handle.
      * @param inputTransaction The transaction in progress.
      * @return whether we should swap the space instead of removing it.
      */
-    private boolean tryStripSpaceAndReturnWhetherShouldSwapInstead(
+    private boolean tryStripSpaceAndReturnWhetherShouldSwapInstead(final Event event,
             final InputTransaction inputTransaction) {
-        final int codePoint = inputTransaction.mEvent.mCodePoint;
-        final boolean isFromSuggestionStrip = inputTransaction.mEvent.isSuggestionStripPress();
+        final int codePoint = event.mCodePoint;
+        final boolean isFromSuggestionStrip = event.isSuggestionStripPress();
         if (Constants.CODE_ENTER == codePoint &&
                 SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) {
             mConnection.removeTrailingSpace();
@@ -1171,14 +1182,16 @@
      * these conditions are fulfilled, this method applies the transformation and returns true.
      * Otherwise, it does nothing and returns false.
      *
+     * @param event The event to handle.
      * @param inputTransaction The transaction in progress.
      * @return true if we applied the double-space-to-period transformation, false otherwise.
      */
-    private boolean tryPerformDoubleSpacePeriod(final InputTransaction inputTransaction) {
+    private boolean tryPerformDoubleSpacePeriod(final Event event,
+            final InputTransaction inputTransaction) {
         // Check the setting, the typed character and the countdown. If any of the conditions is
         // not fulfilled, return false.
         if (!inputTransaction.mSettingsValues.mUseDoubleSpacePeriod
-                || Constants.CODE_SPACE != inputTransaction.mEvent.mCodePoint
+                || Constants.CODE_SPACE != event.mCodePoint
                 || !isDoubleSpacePeriodCountdownActive(inputTransaction)) {
             return false;
         }