Merge "Two words error correction with other error correction for the first word"
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index 3e70896..af16e49 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -58,7 +58,7 @@
         public void requestUpdatingShiftState();
     }
 
-    private KeyboardShiftState mKeyboardShiftState = new KeyboardShiftState();
+    private final SwitchActions mSwitchActions;
 
     private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift");
     private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol");
@@ -72,16 +72,15 @@
     private static final int SWITCH_STATE_CHORDING_ALPHA = 5;
     private static final int SWITCH_STATE_CHORDING_SYMBOL = 6;
     private int mSwitchState = SWITCH_STATE_ALPHA;
-
     private String mLayoutSwitchBackSymbols;
 
-    private final SwitchActions mSwitchActions;
-
     private boolean mIsAlphabetMode;
+    private KeyboardShiftState mAlphabetShiftState = new KeyboardShiftState();
     private boolean mIsSymbolShifted;
+    private boolean mPrevMainKeyboardWasShiftLocked;
+    private boolean mPrevSymbolsKeyboardWasShifted;
 
     private final SavedKeyboardState mSavedKeyboardState = new SavedKeyboardState();
-    private boolean mPrevMainKeyboardWasShiftLocked;
 
     static class SavedKeyboardState {
         public boolean mIsValid;
@@ -99,11 +98,12 @@
             Log.d(TAG, "onLoadKeyboard");
         }
         mLayoutSwitchBackSymbols = layoutSwitchBackSymbols;
-        mKeyboardShiftState.setShifted(false);
-        mKeyboardShiftState.setShiftLocked(false);
+        // Reset alphabet shift state.
+        mAlphabetShiftState.setShiftLocked(false);
+        mPrevMainKeyboardWasShiftLocked = false;
+        mPrevSymbolsKeyboardWasShifted = false;
         mShiftKeyState.onRelease();
         mSymbolKeyState.onRelease();
-        mPrevMainKeyboardWasShiftLocked = false;
         onRestoreKeyboardState();
     }
 
@@ -111,9 +111,9 @@
         final SavedKeyboardState state = mSavedKeyboardState;
         state.mIsAlphabetMode = mIsAlphabetMode;
         if (mIsAlphabetMode) {
-            state.mIsShiftLocked = mKeyboardShiftState.isShiftLocked();
+            state.mIsShiftLocked = mAlphabetShiftState.isShiftLocked();
             state.mIsShifted = !state.mIsShiftLocked
-                    && mKeyboardShiftState.isShiftedOrShiftLocked();
+                    && mAlphabetShiftState.isShiftedOrShiftLocked();
         } else {
             state.mIsShiftLocked = false;
             state.mIsShifted = mIsSymbolShifted;
@@ -155,7 +155,7 @@
 
     // TODO: Remove this method.
     public boolean isShiftLocked() {
-        return mKeyboardShiftState.isShiftLocked();
+        return mAlphabetShiftState.isShiftLocked();
     }
 
     private void setShifted(int shiftMode) {
@@ -164,13 +164,13 @@
         }
         switch (shiftMode) {
         case SwitchActions.AUTOMATIC_SHIFT:
-            mKeyboardShiftState.setAutomaticTemporaryUpperCase();
+            mAlphabetShiftState.setAutomaticTemporaryUpperCase();
             break;
         case SwitchActions.MANUAL_SHIFT:
-            mKeyboardShiftState.setShifted(true);
+            mAlphabetShiftState.setShifted(true);
             break;
         case SwitchActions.UNSHIFT:
-            mKeyboardShiftState.setShifted(false);
+            mAlphabetShiftState.setShifted(false);
             break;
         }
         mSwitchActions.setShifted(shiftMode);
@@ -180,7 +180,7 @@
         if (DEBUG_ACTION) {
             Log.d(TAG, "setShiftLocked: shiftLocked=" + shiftLocked);
         }
-        mKeyboardShiftState.setShiftLocked(shiftLocked);
+        mAlphabetShiftState.setShiftLocked(shiftLocked);
         mSwitchActions.setShiftLocked(shiftLocked);
     }
 
@@ -204,6 +204,7 @@
         if (DEBUG_ACTION) {
             Log.d(TAG, "setAlphabetKeyboard");
         }
+        mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted;
         mSwitchActions.setAlphabetKeyboard();
         mIsAlphabetMode = true;
         mIsSymbolShifted = false;
@@ -215,13 +216,21 @@
 
     // TODO: Make this method private
     public void setSymbolsKeyboard() {
+        mPrevMainKeyboardWasShiftLocked = mAlphabetShiftState.isShiftLocked();
+        if (mPrevSymbolsKeyboardWasShifted) {
+            setSymbolsShiftedKeyboard();
+            return;
+        }
+
         if (DEBUG_ACTION) {
             Log.d(TAG, "setSymbolsKeyboard");
         }
-        mPrevMainKeyboardWasShiftLocked = mKeyboardShiftState.isShiftLocked();
         mSwitchActions.setSymbolsKeyboard();
         mIsAlphabetMode = false;
         mIsSymbolShifted = false;
+        // Reset alphabet shift state.
+        mAlphabetShiftState.setShiftLocked(false);
+        mPrevSymbolsKeyboardWasShifted = false;
         mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
     }
 
@@ -232,6 +241,9 @@
         mSwitchActions.setSymbolsShiftedKeyboard();
         mIsAlphabetMode = false;
         mIsSymbolShifted = true;
+        // Reset alphabet shift state.
+        mAlphabetShiftState.setShiftLocked(false);
+        mPrevSymbolsKeyboardWasShifted = false;
         mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
     }
 
@@ -286,7 +298,7 @@
 
     private void onUpdateShiftStateInternal(boolean autoCaps) {
         if (mIsAlphabetMode) {
-            if (!mKeyboardShiftState.isShiftLocked() && !mShiftKeyState.isIgnoring()) {
+            if (!mAlphabetShiftState.isShiftLocked() && !mShiftKeyState.isIgnoring()) {
                 if (mShiftKeyState.isReleasing() && autoCaps) {
                     // Only when shift key is releasing, automatic temporary upper case will be set.
                     setShifted(SwitchActions.AUTOMATIC_SHIFT);
@@ -304,17 +316,17 @@
 
     private void onPressShift() {
         if (mIsAlphabetMode) {
-            if (mKeyboardShiftState.isShiftLocked()) {
+            if (mAlphabetShiftState.isShiftLocked()) {
                 // Shift key is pressed while caps lock state, we will treat this state as shifted
                 // caps lock state and mark as if shift key pressed while normal state.
                 setShifted(SwitchActions.MANUAL_SHIFT);
                 mShiftKeyState.onPress();
-            } else if (mKeyboardShiftState.isAutomaticTemporaryUpperCase()) {
+            } else if (mAlphabetShiftState.isAutomaticTemporaryUpperCase()) {
                 // Shift key is pressed while automatic temporary upper case, we have to move to
                 // manual temporary upper case.
                 setShifted(SwitchActions.MANUAL_SHIFT);
                 mShiftKeyState.onPress();
-            } else if (mKeyboardShiftState.isShiftedOrShiftLocked()) {
+            } else if (mAlphabetShiftState.isShiftedOrShiftLocked()) {
                 // In manual upper case state, we just record shift key has been pressing while
                 // shifted state.
                 mShiftKeyState.onPressOnShifted();
@@ -333,22 +345,26 @@
 
     private void onReleaseShift(boolean withSliding) {
         if (mIsAlphabetMode) {
-            final boolean isShiftLocked = mKeyboardShiftState.isShiftLocked();
+            final boolean isShiftLocked = mAlphabetShiftState.isShiftLocked();
             if (mShiftKeyState.isMomentary()) {
                 // After chording input while normal state.
-                setShifted(SwitchActions.UNSHIFT);
-            } else if (isShiftLocked && !mKeyboardShiftState.isShiftLockShifted()
+                if (mAlphabetShiftState.isShiftLockShifted()) {
+                    setShiftLocked(true);
+                } else {
+                    setShifted(SwitchActions.UNSHIFT);
+                }
+            } else if (isShiftLocked && !mAlphabetShiftState.isShiftLockShifted()
                     && (mShiftKeyState.isPressing() || mShiftKeyState.isPressingOnShifted())
                     && !withSliding) {
                 // Shift has been long pressed, ignore this release.
             } else if (isShiftLocked && !mShiftKeyState.isIgnoring() && !withSliding) {
                 // Shift has been pressed without chording while caps lock state.
                 setShiftLocked(false);
-            } else if (mKeyboardShiftState.isShiftedOrShiftLocked()
+            } else if (mAlphabetShiftState.isShiftedOrShiftLocked()
                     && mShiftKeyState.isPressingOnShifted() && !withSliding) {
                 // Shift has been pressed without chording while shifted state.
                 setShifted(SwitchActions.UNSHIFT);
-            } else if (mKeyboardShiftState.isManualTemporaryUpperCaseFromAuto()
+            } else if (mAlphabetShiftState.isManualTemporaryUpperCaseFromAuto()
                     && mShiftKeyState.isPressing() && !withSliding) {
                 // Shift has been pressed without chording while manual temporary upper case
                 // transited from automatic temporary upper case.
@@ -401,7 +417,7 @@
         }
 
         if (mIsAlphabetMode && code == Keyboard.CODE_CAPSLOCK) {
-            if (mKeyboardShiftState.isShiftLocked()) {
+            if (mAlphabetShiftState.isShiftLocked()) {
                 setShiftLocked(false);
                 // Shift key is long pressed or double tapped while caps lock state, we will
                 // toggle back to normal state. And mark as if shift key is released.
@@ -503,7 +519,8 @@
 
     @Override
     public String toString() {
-        return "[keyboard=" + mKeyboardShiftState
+        return "[keyboard=" + (mIsAlphabetMode ? mAlphabetShiftState.toString()
+                        : (mIsSymbolShifted ? "SYMBOLS_SHIFTED" : "SYMBOLS"))
                 + " shift=" + mShiftKeyState
                 + " symbol=" + mSymbolKeyState
                 + " switch=" + switchStateToString(mSwitchState) + "]";
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index d11aaeb..d36140d 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1924,8 +1924,7 @@
             // Updating the predictions right away may be slow and feel unresponsive on slower
             // terminals. On the other hand if we just postUpdateBigramPredictions() it will
             // take a noticeable delay to update them which may feel uneasy.
-        }
-        if (showingAddToDictionaryHint) {
+        } else {
             if (mIsUserDictionaryAvailable) {
                 mSuggestionsView.showAddToDictionaryHint(
                         suggestion, mSettingsValues.mHintToSaveText);
@@ -1942,9 +1941,6 @@
      * Commits the chosen word to the text field and saves it for later retrieval.
      */
     private void commitChosenWord(final CharSequence bestWord, final int commitType) {
-        final KeyboardSwitcher switcher = mKeyboardSwitcher;
-        if (!switcher.isKeyboardAvailable())
-            return;
         final InputConnection ic = getCurrentInputConnection();
         if (ic != null) {
             mVoiceProxy.rememberReplacedWord(bestWord, mSettingsValues.mWordSeparators);
@@ -2158,6 +2154,16 @@
 
     // "ic" must not be null
     private void restartSuggestionsOnManuallyPickedTypedWord(final InputConnection ic) {
+        // Note: this relies on the last word still being held in the WordComposer, in
+        // the field for suggestion resuming.
+        // Note: in the interest of code simplicity, we may want to just call
+        // restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving
+        // the old WordComposer allows to reuse the actual typed coordinates.
+        mWordComposer.resumeSuggestionOnKeptWord();
+        // We resume suggestion, and then we want to set the composing text to the content
+        // of the word composer again. But since we just manually picked a word, there is
+        // no composing text at the moment, so we have to delete the word before we set a
+        // new composing text.
         final int restartLength = mWordComposer.size();
         if (DEBUG) {
             final String wordBeforeCursor =
@@ -2171,13 +2177,8 @@
                         + wordBeforeCursor + "\"");
             }
         }
+        // Warning: this +1 takes into account the extra space added by the manual pick process.
         ic.deleteSurroundingText(restartLength + 1, 0);
-
-        // Note: this relies on the last word still being held in the WordComposer
-        // Note: in the interest of code simplicity, we may want to just call
-        // restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving
-        // the old WordComposer allows to reuse the actual typed coordinates.
-        mWordComposer.resumeSuggestionOnKeptWord();
         ic.setComposingText(mWordComposer.getTypedWord(), 1);
         mHandler.cancelUpdateBigramPredictions();
         mHandler.postUpdateSuggestions();
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java
index 6fbe769..729120b 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java
@@ -52,19 +52,18 @@
         chordingPressAndReleaseKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
         chordingPressAndReleaseKey('X', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
 
-        // TODO: This test fails due to bug, though external behavior is correct.
         // Release shift key, switch back to alphabet shift locked.
-//        releaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCKED);
-//
-//        // Press symbols key and hold, enter into choring symbols state.
-//        pressKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED);
-//
-//        // Press/release symbol letter keys.
-//        chordingPressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-//        chordingPressAndReleaseKey('2', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-//
-//        // Release symbols key, switch back to alphabet shift locked.
-//        releaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED);
+        releaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCKED);
+
+        // Press symbols key and hold, enter into choring symbols state.
+        pressKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED);
+
+        // Press/release symbol letter keys.
+        chordingPressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        chordingPressAndReleaseKey('2', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+
+        // Release symbols key, switch back to alphabet shift locked.
+        releaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED);
     }
 
     // Symbols key chording input.
@@ -110,16 +109,15 @@
         // Release shift key, switch back to symbols shifted state.
         releaseKey(CODE_SHIFT, SYMBOLS_SHIFTED);
 
-        // TODO: This test fails due to bug.
-//        // Press "ABC" key and hold, enter into choring alphabet state.
-//        pressKey(CODE_SYMBOL, ALPHABET_UNSHIFTED);
-//
-//        // Press/release letter keys.
-//        chordingPressAndReleaseKey('a', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
-//        chordingPressAndReleaseKey('b', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
-//
-//        // Release "ABC" key, switch back to symbols.
-//        releaseKey(CODE_SYMBOL, SYMBOLS_SHIFTED);
+        // Press "ABC" key and hold, enter into choring alphabet state.
+        pressKey(CODE_SYMBOL, ALPHABET_UNSHIFTED);
+
+        // Press/release letter keys.
+        chordingPressAndReleaseKey('a', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+        chordingPressAndReleaseKey('b', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
+
+        // Release "ABC" key, switch back to symbols.
+        releaseKey(CODE_SYMBOL, SYMBOLS_SHIFTED);
     }
 
     // Chording shift key in automatic upper case.