diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 10f651ad15ff7ae9b1c4efafc93788385df5777b..0232fae4f8cafea3e89e32c889e73e430308d3d0 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -39,6 +39,7 @@ import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SettingsValues;
 import com.android.inputmethod.latin.SubtypeSwitcher;
 import com.android.inputmethod.latin.Utils;
+import com.android.inputmethod.latin.WordComposer;
 
 public class KeyboardSwitcher implements KeyboardState.SwitchActions {
     private static final String TAG = KeyboardSwitcher.class.getSimpleName();
@@ -402,4 +403,16 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
             }
         }
     }
+
+    public int getManualCapsMode() {
+        switch (getKeyboard().mId.mElementId) {
+        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
+        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
+            return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED;
+        case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
+            return WordComposer.CAPS_MODE_MANUAL_SHIFTED;
+        default:
+            return WordComposer.CAPS_MODE_OFF;
+        }
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index a7896ad52685a3fa27a4806815c972476a7a0a8d..f5dc54c414ef293eafb7a0e0fa74124c1ccf0838 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1093,6 +1093,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         return mConnection.getCursorCapsMode(inputType);
     }
 
+    // Factor in auto-caps and manual caps and compute the current caps mode.
+    private int getActualCapsMode() {
+        final int manual = mKeyboardSwitcher.getManualCapsMode();
+        if (manual != WordComposer.CAPS_MODE_OFF) return manual;
+        final int auto = getCurrentAutoCapsState();
+        if (0 != (auto & TextUtils.CAP_MODE_CHARACTERS)) {
+            return WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED;
+        }
+        if (0 != auto) return WordComposer.CAPS_MODE_AUTO_SHIFTED;
+        return WordComposer.CAPS_MODE_OFF;
+    }
+
     private void swapSwapperAndSpace() {
         CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0);
         // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
@@ -1377,8 +1389,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         }
         mConnection.endBatchEdit();
         // TODO: Should handle TextUtils.CAP_MODE_CHARACTER.
-        mWordComposer.setAutoCapitalized(
-                getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF);
+        mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
     }
 
     @Override
@@ -1613,8 +1624,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
             mWordComposer.add(primaryCode, keyX, keyY);
             // If it's the first letter, make note of auto-caps state
             if (mWordComposer.size() == 1) {
-                mWordComposer.setAutoCapitalized(
-                        getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF);
+                mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
             }
             mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
         } else {
@@ -2014,7 +2024,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
             final CharSequence prevWord
                     = mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, 2);
             final String secondWord;
-            if (mWordComposer.isAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
+            if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
                 secondWord = suggestion.toString().toLowerCase(
                         mSubtypeSwitcher.getCurrentSubtypeLocale());
             } else {
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index e9314084ab1b5704d3d703b87feb5b396cb7b98b..6a4562c49a0a88bfa82e0d41f0e817f1e09d6c8c 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -309,7 +309,7 @@ public class Suggest {
         final ArrayList<SuggestedWordInfo> suggestionsContainer =
                 new ArrayList<SuggestedWordInfo>(suggestionsSet);
         final int suggestionsCount = suggestionsContainer.size();
-        final boolean isFirstCharCapitalized = wordComposer.isAutoCapitalized();
+        final boolean isFirstCharCapitalized = wordComposer.wasAutoCapitalized();
         // TODO: Handle the manual temporary shifted mode.
         // TODO: Should handle TextUtils.CAP_MODE_CHARACTER.
         final boolean isAllUpperCase = false;
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 5606a58e4f14308e954abef3be3104fc2c7976db..0580d89543f8f8c17cf23959eadb3af0b13ed9eb 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -32,6 +32,14 @@ public class WordComposer {
 
     private static final int N = BinaryDictionary.MAX_WORD_LENGTH;
 
+    public static final int CAPS_MODE_OFF = 0;
+    // 1 is shift bit, 2 is caps bit, 4 is auto bit but this is just a convention as these bits
+    // aren't used anywhere in the code
+    public static final int CAPS_MODE_MANUAL_SHIFTED = 0x1;
+    public static final int CAPS_MODE_MANUAL_SHIFT_LOCKED = 0x3;
+    public static final int CAPS_MODE_AUTO_SHIFTED = 0x5;
+    public static final int CAPS_MODE_AUTO_SHIFT_LOCKED = 0x7;
+
     private int[] mPrimaryKeyCodes;
     private final InputPointers mInputPointers = new InputPointers(N);
     private final StringBuilder mTypedWord;
@@ -42,7 +50,7 @@ public class WordComposer {
     // Cache these values for performance
     private int mCapsCount;
     private int mDigitsCount;
-    private boolean mAutoCapitalized;
+    private int mCapitalizedMode;
     private int mTrailingSingleQuotesCount;
     private int mCodePointSize;
 
@@ -68,7 +76,7 @@ public class WordComposer {
         mCapsCount = source.mCapsCount;
         mDigitsCount = source.mDigitsCount;
         mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
-        mAutoCapitalized = source.mAutoCapitalized;
+        mCapitalizedMode = source.mCapitalizedMode;
         mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount;
         mIsResumed = source.mIsResumed;
         mIsBatchMode = source.mIsBatchMode;
@@ -280,20 +288,27 @@ public class WordComposer {
     }
 
     /**
-     * Saves the reason why the word is capitalized - whether it was automatic or
-     * due to the user hitting shift in the middle of a sentence.
-     * @param auto whether it was an automatic capitalization due to start of sentence
+     * Saves the caps mode at the start of composing.
+     *
+     * WordComposer needs to know about this for several reasons. The first is, we need to know
+     * after the fact what the reason was, to register the correct form into the user history
+     * dictionary: if the word was automatically capitalized, we should insert it in all-lower
+     * case but if it's a manual pressing of shift, then it should be inserted as is.
+     * Also, batch input needs to know about the current caps mode to display correctly
+     * capitalized suggestions.
+     * @param mode the mode at the time of start
      */
-    public void setAutoCapitalized(boolean auto) {
-        mAutoCapitalized = auto;
+    public void setCapitalizedModeAtStartComposingTime(final int mode) {
+        mCapitalizedMode = mode;
     }
 
     /**
      * Returns whether the word was automatically capitalized.
      * @return whether the word was automatically capitalized
      */
-    public boolean isAutoCapitalized() {
-        return mAutoCapitalized;
+    public boolean wasAutoCapitalized() {
+        return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
+                || mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED;
     }
 
     /**