Merge "Revert "Introduce onEvent() to improve testability"" into lmp-dev
diff --git a/java/res/drawable/btn_keyboard_key_lxx_dark.xml b/java/res/drawable/btn_keyboard_key_lxx_dark.xml
index bb1789a..c82c138 100644
--- a/java/res/drawable/btn_keyboard_key_lxx_dark.xml
+++ b/java/res/drawable/btn_keyboard_key_lxx_dark.xml
@@ -15,6 +15,11 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Custom label action keys. -->
+    <item android:state_active="true" android:state_checked="true" android:state_pressed="true"
+          android:drawable="@color/key_background_pressed_lxx_dark" />
+    <item android:state_active="true" android:state_checked="true"
+          android:drawable="@color/key_background_lxx_dark" />
     <!-- Action keys. -->
     <item android:state_active="true" android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_active_pressed_lxx_dark" />
diff --git a/java/res/drawable/btn_keyboard_key_lxx_light.xml b/java/res/drawable/btn_keyboard_key_lxx_light.xml
index 60fe02d..f237fbe 100644
--- a/java/res/drawable/btn_keyboard_key_lxx_light.xml
+++ b/java/res/drawable/btn_keyboard_key_lxx_light.xml
@@ -15,6 +15,11 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Custom label action keys. -->
+    <item android:state_active="true" android:state_checked="true" android:state_pressed="true"
+          android:drawable="@color/key_background_pressed_lxx_light" />
+    <item android:state_active="true" android:state_checked="true"
+          android:drawable="@color/key_background_lxx_light" />
     <!-- Action keys. -->
     <item android:state_active="true" android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_active_pressed_lxx_light" />
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 9a22273..e6215c2 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -270,9 +270,10 @@
             <enum name="empty" value="0" />
             <enum name="normal" value="1" />
             <enum name="functional" value="2" />
-            <enum name="action" value="3" />
-            <enum name="stickyOff" value="4" />
-            <enum name="stickyOn" value="5" />
+            <enum name="stickyOff" value="3" />
+            <enum name="stickyOn" value="4" />
+            <enum name="action" value="5" />
+            <enum name="customAction" value="6" />
         </attr>
         <!-- The key action flags. -->
         <attr name="keyActionFlags" format="integer">
diff --git a/java/res/xml-sw600dp/key_styles_enter.xml b/java/res/xml-sw600dp/key_styles_enter.xml
index 740bf35..d0167d3 100644
--- a/java/res/xml-sw600dp/key_styles_enter.xml
+++ b/java/res/xml-sw600dp/key_styles_enter.xml
@@ -230,6 +230,7 @@
                 latin:styleName="enterKeyStyle"
                 latin:keySpec="dummy_label|!code/key_enter"
                 latin:keyLabelFlags="fromCustomActionLabel"
+                latin:backgroundType="customAction"
                 latin:parentStyle="defaultEnterKeyStyle" />
         </case>
         <!-- imeAction is either actionNone or actionUnspecified. -->
diff --git a/java/res/xml/key_styles_enter.xml b/java/res/xml/key_styles_enter.xml
index 770bf38..960c79c 100644
--- a/java/res/xml/key_styles_enter.xml
+++ b/java/res/xml/key_styles_enter.xml
@@ -398,6 +398,7 @@
                 latin:styleName="enterKeyStyle"
                 latin:keySpec="dummy_label|!code/key_enter"
                 latin:keyLabelFlags="fromCustomActionLabel"
+                latin:backgroundType="customAction"
                 latin:parentStyle="defaultEnterKeyStyle" />
         </case>
         <!-- imeAction is either actionNone or actionUnspecified. -->
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 5021f33..cfe25d1 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -51,7 +51,7 @@
     fr_CH: French (Switzerland)/swiss
     gl_ES: Galician (Spain)/spanish
     hi: Hindi/hindi
-    (hi: Hindi/hindi_compact) # This is a preliminary keyboard layout.
+    hi: Hindi/hindi_compact
     hr: Croatian/qwertz
     hu: Hungarian/qwertz
     hy_AM: Armenian (Armenia) Phonetic/armenian_phonetic
@@ -346,8 +346,6 @@
             android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi,EmojiCapable"
             android:isAsciiCapable="false"
     />
-    <!-- TODO: This hindi_compact keyboard is a preliminary layout.
-               This isn't based on the final specification. -->
     <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic_compact"
             android:subtypeId="0xe49c89a1"
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index af54fb6..55ce7dd 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -123,9 +123,10 @@
     public static final int BACKGROUND_TYPE_EMPTY = 0;
     public static final int BACKGROUND_TYPE_NORMAL = 1;
     public static final int BACKGROUND_TYPE_FUNCTIONAL = 2;
-    public static final int BACKGROUND_TYPE_ACTION = 3;
-    public static final int BACKGROUND_TYPE_STICKY_OFF = 4;
-    public static final int BACKGROUND_TYPE_STICKY_ON = 5;
+    public static final int BACKGROUND_TYPE_STICKY_OFF = 3;
+    public static final int BACKGROUND_TYPE_STICKY_ON = 4;
+    public static final int BACKGROUND_TYPE_ACTION = 5;
+    public static final int BACKGROUND_TYPE_CUSTOM_ACTION = 6;
 
     private final int mActionFlags;
     private static final int ACTION_FLAGS_IS_REPEATABLE = 0x01;
@@ -483,9 +484,10 @@
         case BACKGROUND_TYPE_EMPTY: return "empty";
         case BACKGROUND_TYPE_NORMAL: return "normal";
         case BACKGROUND_TYPE_FUNCTIONAL: return "functional";
-        case BACKGROUND_TYPE_ACTION: return "action";
         case BACKGROUND_TYPE_STICKY_OFF: return "stickyOff";
         case BACKGROUND_TYPE_STICKY_ON: return "stickyOn";
+        case BACKGROUND_TYPE_ACTION: return "action";
+        case BACKGROUND_TYPE_CUSTOM_ACTION: return "customAction";
         default: return null;
         }
     }
@@ -814,47 +816,37 @@
         return dx * dx + dy * dy;
     }
 
-    private final static int[] KEY_STATE_NORMAL_HIGHLIGHT_ON = {
-        android.R.attr.state_checkable,
-        android.R.attr.state_checked
-    };
+    static class KeyBackgroundState {
+        private final int[] mReleasedState;
+        private final int[] mPressedState;
 
-    private final static int[] KEY_STATE_PRESSED_HIGHLIGHT_ON = {
-        android.R.attr.state_pressed,
-        android.R.attr.state_checkable,
-        android.R.attr.state_checked
-    };
+        private KeyBackgroundState(final int ... attrs) {
+            mReleasedState = attrs;
+            mPressedState = Arrays.copyOf(attrs, attrs.length + 1);
+            mPressedState[attrs.length] = android.R.attr.state_pressed;
+        }
 
-    private final static int[] KEY_STATE_NORMAL_HIGHLIGHT_OFF = {
-        android.R.attr.state_checkable
-    };
+        public int[] getState(final boolean pressed) {
+            return pressed ? mPressedState : mReleasedState;
+        }
 
-    private final static int[] KEY_STATE_PRESSED_HIGHLIGHT_OFF = {
-        android.R.attr.state_pressed,
-        android.R.attr.state_checkable
-    };
-
-    private final static int[] KEY_STATE_NORMAL = {
-    };
-
-    private final static int[] KEY_STATE_PRESSED = {
-        android.R.attr.state_pressed
-    };
-
-    private final static int[] KEY_STATE_EMPTY = {
-        android.R.attr.state_empty
-    };
-
-    // action normal state (with properties)
-    private static final int[] KEY_STATE_ACTIVE_NORMAL = {
-            android.R.attr.state_active
-    };
-
-    // action pressed state (with properties)
-    private static final int[] KEY_STATE_ACTIVE_PRESSED = {
-            android.R.attr.state_active,
-            android.R.attr.state_pressed
-    };
+        public static final KeyBackgroundState[] STATES = {
+            // 0: BACKGROUND_TYPE_EMPTY
+            new KeyBackgroundState(android.R.attr.state_empty),
+            // 1: BACKGROUND_TYPE_NORMAL
+            new KeyBackgroundState(),
+            // 2: BACKGROUND_TYPE_FUNCTIONAL
+            new KeyBackgroundState(),
+            // 3: BACKGROUND_TYPE_STICKY_OFF
+            new KeyBackgroundState(android.R.attr.state_checkable),
+            // 4: BACKGROUND_TYPE_STICKY_ON
+            new KeyBackgroundState(android.R.attr.state_checkable, android.R.attr.state_checked),
+            // 5: BACKGROUND_TYPE_ACTION
+            new KeyBackgroundState(android.R.attr.state_active),
+            // 6: BACKGROUND_TYPE_CUSTOM_ACTION
+            new KeyBackgroundState(android.R.attr.state_active, android.R.attr.state_checked)
+        };
+    }
 
     /**
      * Returns the background drawable for the key, based on the current state and type of the key.
@@ -871,28 +863,8 @@
         } else {
             background = keyBackground;
         }
-        final int[] stateSet;
-        switch (mBackgroundType) {
-        case BACKGROUND_TYPE_ACTION:
-            stateSet = mPressed ? KEY_STATE_ACTIVE_PRESSED : KEY_STATE_ACTIVE_NORMAL;
-            break;
-        case BACKGROUND_TYPE_STICKY_OFF:
-            stateSet = mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_OFF : KEY_STATE_NORMAL_HIGHLIGHT_OFF;
-            break;
-        case BACKGROUND_TYPE_STICKY_ON:
-            stateSet = mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_ON : KEY_STATE_NORMAL_HIGHLIGHT_ON;
-            break;
-        case BACKGROUND_TYPE_EMPTY:
-            stateSet = mPressed ? KEY_STATE_PRESSED : KEY_STATE_EMPTY;
-            break;
-        case BACKGROUND_TYPE_FUNCTIONAL:
-            stateSet = mPressed ? KEY_STATE_PRESSED : KEY_STATE_NORMAL;
-            break;
-        default: /* BACKGROUND_TYPE_NORMAL */
-            stateSet = mPressed ? KEY_STATE_PRESSED : KEY_STATE_NORMAL;
-            break;
-        }
-        background.setState(stateSet);
+        final int[] state = KeyBackgroundState.STATES[mBackgroundType].getState(mPressed);
+        background.setState(state);
         return background;
     }
 
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index c7c3aaa..3a5f85d 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -295,8 +295,7 @@
             }
         }
 
-        public void postResetInputConnectionCaches(final boolean tryResumeSuggestions,
-                final int remainingTries) {
+        public void postResetCaches(final boolean tryResumeSuggestions, final int remainingTries) {
             removeMessages(MSG_RESET_CACHES);
             sendMessage(obtainMessage(MSG_RESET_CACHES, tryResumeSuggestions ? 1 : 0,
                     remainingTries, null));
@@ -753,92 +752,20 @@
         loadKeyboard();
     }
 
-    /**
-     * A class that holds information to pass from onStartInputInternal to onStartInputViewInternal
-     *
-     * OnStartInput needs to reload the settings and that will prevent onStartInputViewInternal
-     * from comparing the old settings with the new ones, so we use this memory to pass the
-     * necessary information along.
-     */
-    private static class EditorChangeInfo {
-        public final boolean mIsSameInputType;
-        public final boolean mHasSameOrientation;
-        public final boolean mCanReachInputConnection;
-        public EditorChangeInfo(final boolean isSameInputType, final boolean hasSameOrientation,
-                final boolean canReachInputConnection) {
-            mIsSameInputType = isSameInputType;
-            mHasSameOrientation = hasSameOrientation;
-            mCanReachInputConnection = canReachInputConnection;
-        }
-    }
-
-    private EditorChangeInfo mLastEditorChangeInfo;
-
     private void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) {
         super.onStartInput(editorInfo, restarting);
-        if (editorInfo == null) {
-            Log.e(TAG, "Null EditorInfo in onStartInput()");
-            return;
-        }
-        SettingsValues currentSettingsValues = mSettings.getCurrent();
-        final boolean isSameInputType = currentSettingsValues.isSameInputType(editorInfo);
-        final boolean hasSameOrientation =
-                currentSettingsValues.hasSameOrientation(getResources().getConfiguration());
-        mRichImm.clearSubtypeCaches();
-        final boolean inputTypeChanged = !isSameInputType;
-        final boolean isDifferentTextField = !restarting || inputTypeChanged;
-        if (isDifferentTextField || !hasSameOrientation) {
-            loadSettings();
-            currentSettingsValues = mSettings.getCurrent();
-        }
-
-        // Note: the following does a round-trip IPC on the main thread: be careful
-        final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
-        final Suggest suggest = mInputLogic.mSuggest;
-        if (null != currentLocale && !currentLocale.equals(suggest.getLocale())) {
-            // TODO: Do this automatically.
-            resetSuggest();
-        }
-        if (isDifferentTextField && currentSettingsValues.mAutoCorrectionEnabledPerUserSettings) {
-            suggest.setAutoCorrectionThreshold(currentSettingsValues.mAutoCorrectionThreshold);
-        }
-
-        // The app calling setText() has the effect of clearing the composing
-        // span, so we should reset our state unconditionally, even if restarting is true.
-        // We also tell the input logic about the combining rules for the current subtype, so
-        // it can adjust its combiners if needed.
-        mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype());
-        // TODO[IL]: Can the following be moved to InputLogic#startInput?
-        final boolean canReachInputConnection;
-        if (!mInputLogic.mConnection.resetCachesUponCursorMoveAndReturnSuccess(
-                editorInfo.initialSelStart, editorInfo.initialSelEnd,
-                false /* shouldFinishComposition */)) {
-            // Sometimes, while rotating, for some reason the framework tells the app we are not
-            // connected to it and that means we can't refresh the cache. In this case, schedule a
-            // refresh later.
-            // We try resetting the caches up to 5 times before giving up.
-            mHandler.postResetInputConnectionCaches(isDifferentTextField || !hasSameOrientation,
-                    5 /* remainingTries */);
-            canReachInputConnection = false;
-        } else {
-            // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best
-            // effort to work around this bug.
-            mInputLogic.mConnection.tryFixLyingCursorPosition();
-            mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
-                    true /* shouldDelay */);
-            canReachInputConnection = true;
-        }
-
-        mLastEditorChangeInfo = new EditorChangeInfo(isSameInputType, hasSameOrientation,
-                canReachInputConnection);
     }
 
     @SuppressWarnings("deprecation")
     private void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) {
         super.onStartInputView(editorInfo, restarting);
+        mRichImm.clearSubtypeCaches();
         final KeyboardSwitcher switcher = mKeyboardSwitcher;
         switcher.updateKeyboardTheme();
         final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView();
+        // If we are starting input in a different text field from before, we'll have to reload
+        // settings, so currentSettingsValues can't be final.
+        SettingsValues currentSettingsValues = mSettings.getCurrent();
 
         if (editorInfo == null) {
             Log.e(TAG, "Null EditorInfo in onStartInputView()");
@@ -881,7 +808,7 @@
             accessUtils.onStartInputViewInternal(mainKeyboardView, editorInfo, restarting);
         }
 
-        final boolean inputTypeChanged = !mLastEditorChangeInfo.mIsSameInputType;
+        final boolean inputTypeChanged = !currentSettingsValues.isSameInputType(editorInfo);
         final boolean isDifferentTextField = !restarting || inputTypeChanged;
         if (isDifferentTextField) {
             mSubtypeSwitcher.updateParametersOnStartInputView();
@@ -891,13 +818,57 @@
         // Note: This call should be done by InputMethodService?
         updateFullscreenMode();
 
-        final SettingsValues currentSettingsValues = mSettings.getCurrent();
+        // The app calling setText() has the effect of clearing the composing
+        // span, so we should reset our state unconditionally, even if restarting is true.
+        // We also tell the input logic about the combining rules for the current subtype, so
+        // it can adjust its combiners if needed.
+        mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype());
+
+        // Note: the following does a round-trip IPC on the main thread: be careful
+        final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
+        final Suggest suggest = mInputLogic.mSuggest;
+        if (null != currentLocale && !currentLocale.equals(suggest.getLocale())) {
+            // TODO: Do this automatically.
+            resetSuggest();
+        }
+
+        // TODO[IL]: Can the following be moved to InputLogic#startInput?
+        final boolean canReachInputConnection;
+        if (!mInputLogic.mConnection.resetCachesUponCursorMoveAndReturnSuccess(
+                editorInfo.initialSelStart, editorInfo.initialSelEnd,
+                false /* shouldFinishComposition */)) {
+            // Sometimes, while rotating, for some reason the framework tells the app we are not
+            // connected to it and that means we can't refresh the cache. In this case, schedule a
+            // refresh later.
+            // We try resetting the caches up to 5 times before giving up.
+            mHandler.postResetCaches(isDifferentTextField, 5 /* remainingTries */);
+            // mLastSelection{Start,End} are reset later in this method, don't need to do it here
+            canReachInputConnection = false;
+        } else {
+            // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best
+            // effort to work around this bug.
+            mInputLogic.mConnection.tryFixLyingCursorPosition();
+            mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
+                    true /* shouldDelay */);
+            canReachInputConnection = true;
+        }
+
+        if (isDifferentTextField ||
+                !currentSettingsValues.hasSameOrientation(getResources().getConfiguration())) {
+            loadSettings();
+        }
         if (isDifferentTextField) {
             mainKeyboardView.closing();
+            currentSettingsValues = mSettings.getCurrent();
+
+            if (currentSettingsValues.mAutoCorrectionEnabledPerUserSettings) {
+                suggest.setAutoCorrectionThreshold(
+                        currentSettingsValues.mAutoCorrectionThreshold);
+            }
 
             switcher.loadKeyboard(editorInfo, currentSettingsValues, getCurrentAutoCapsState(),
                     getCurrentRecapitalizeState());
-            if (!mLastEditorChangeInfo.mCanReachInputConnection) {
+            if (!canReachInputConnection) {
                 // If we can't reach the input connection, we will call loadKeyboard again later,
                 // so we need to save its state now. The call will be done in #retryResetCaches.
                 switcher.saveKeyboardState();
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 2be7920..418866a 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -2035,7 +2035,7 @@
                 mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd(),
                 shouldFinishComposition)) {
             if (0 < remainingTries) {
-                handler.postResetInputConnectionCaches(tryResumeSuggestions, remainingTries - 1);
+                handler.postResetCaches(tryResumeSuggestions, remainingTries - 1);
                 return false;
             }
             // If remainingTries is 0, we should stop waiting for new tries, however we'll still
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java
index 61eae4e..ab69c85 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.latin;
 
 import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
 import android.util.Pair;
 
 /*
@@ -77,6 +78,8 @@
  */
 
 @LargeTest
+// These tests are inactive until the combining code for Myanmar Reordering is sorted out.
+@Suppress
 @SuppressWarnings("rawtypes")
 public class InputLogicTestsReorderingMyanmar extends InputTestsBase {
     // The tests are formatted as follows.