diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index db8f269eb5e1b533cf11ac791ef0b9eae252a8e2..b13826bfc63cf0abefc1aa4855fe8d06f2e06693 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -741,6 +741,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         // switcher.loadKeyboard; in apps like Talk, we come here when the text is sent and the
         // field gets emptied and we need to re-evaluate the shift state, but not the whole layout
         // which would be disruptive.
+        // Space state must be updated before calling updateShiftState
         mKeyboardSwitcher.updateShiftState();
 
         mHandler.cancelUpdateSuggestionStrip();
@@ -1114,11 +1115,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         // unless needed.
         if (mWordComposer.isComposingWord()) return Constants.TextUtils.CAP_MODE_OFF;
 
-        // TODO: This blocking IPC call is heavy. Consider doing this without using IPC calls.
-        // Note: getCursorCapsMode() returns the current capitalization mode that is any
-        // combination of CAP_MODE_CHARACTERS, CAP_MODE_WORDS, and CAP_MODE_SENTENCES. 0 means none
-        // of them.
-        return mConnection.getCursorCapsMode(inputType, mSubtypeSwitcher.getCurrentSubtypeLocale());
+        // Warning: this depends on mSpaceState, which may not be the most current value. If
+        // mSpaceState gets updated later, whoever called this may need to be told about it.
+        return mConnection.getCursorCapsMode(inputType, mSubtypeSwitcher.getCurrentSubtypeLocale(),
+                SPACE_STATE_PHANTOM == mSpaceState);
     }
 
     // Factor in auto-caps and manual caps and compute the current caps mode.
@@ -1391,9 +1391,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         }
         mConnection.commitText(text, 1);
         mConnection.endBatchEdit();
+        // Space state must be updated before calling updateShiftState
+        mSpaceState = SPACE_STATE_NONE;
         mKeyboardSwitcher.updateShiftState();
         mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT);
-        mSpaceState = SPACE_STATE_NONE;
         mEnteredText = text;
     }
 
@@ -1509,8 +1510,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         mConnection.setComposingText(batchInputText, 1);
         mExpectingUpdateSelection = true;
         mConnection.endBatchEdit();
-        mKeyboardSwitcher.updateShiftState();
+        // Space state must be updated before calling updateShiftState
         mSpaceState = SPACE_STATE_PHANTOM;
+        mKeyboardSwitcher.updateShiftState();
     }
 
     private CharSequence specificTldProcessingOnTextInput(final CharSequence text) {
@@ -2019,8 +2021,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         mConnection.endBatchEdit();
         // Don't allow cancellation of manual pick
         mLastComposedWord.deactivate();
+        // Space state must be updated before calling updateShiftState
         mSpaceState = SPACE_STATE_PHANTOM;
-        // TODO: is this necessary?
         mKeyboardSwitcher.updateShiftState();
 
         // We should show the "Touch again to save" hint if the user pressed the first entry
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index b85f9dcd70f52bbacbe3b0a2444cfc417557c97d..cbc6a93fa731edbd2ce0840bcc062e73bcdadebb 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -190,7 +190,23 @@ public class RichInputConnection {
         }
     }
 
-    public int getCursorCapsMode(final int inputType, final Locale locale) {
+    /**
+     * Gets the caps modes we should be in after this specific string.
+     *
+     * This returns a bit set of TextUtils#CAP_MODE_*, masked by the inputType argument.
+     * This method also supports faking an additional space after the string passed in argument,
+     * to support cases where a space will be added automatically, like in phantom space
+     * state for example.
+     * Note that for English, we are using American typography rules (which are not specific to
+     * American English, it's just the most common set of rules for English).
+     *
+     * @param inputType a mask of the caps modes to test for.
+     * @param locale what language should be considered.
+     * @param hasSpaceBefore if we should consider there should be a space after the string.
+     * @return the caps modes that should be on as a set of bits
+     */
+    public int getCursorCapsMode(final int inputType, final Locale locale,
+            final boolean hasSpaceBefore) {
         mIC = mParent.getCurrentInputConnection();
         if (null == mIC) return Constants.TextUtils.CAP_MODE_OFF;
         if (!TextUtils.isEmpty(mComposingText)) return Constants.TextUtils.CAP_MODE_OFF;
@@ -205,7 +221,8 @@ public class RichInputConnection {
         }
         // This never calls InputConnection#getCapsMode - in fact, it's a static method that
         // never blocks or initiates IPC.
-        return StringUtils.getCapsMode(mCommittedTextBeforeComposingText, inputType, locale);
+        return StringUtils.getCapsMode(mCommittedTextBeforeComposingText, inputType, locale,
+                hasSpaceBefore);
     }
 
     public CharSequence getTextBeforeCursor(final int i, final int j) {
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index 6dc1ea807e1524066bb592220dec4634939d36ba..7b65b73435e19bf195ed258f362a49f386e8e5d5 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -197,13 +197,15 @@ public final class StringUtils {
      * {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and
      * {@link TextUtils#CAP_MODE_SENTENCES}.
      * @param locale The locale to consider for capitalization rules
+     * @param hasSpaceBefore Whether we should consider there is a space inserted at the end of cs
      *
      * @return Returns the actual capitalization modes that can be in effect
      * at the current position, which is any combination of
      * {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and
      * {@link TextUtils#CAP_MODE_SENTENCES}.
      */
-    public static int getCapsMode(final CharSequence cs, final int reqModes, final Locale locale) {
+    public static int getCapsMode(final CharSequence cs, final int reqModes, final Locale locale,
+            final boolean hasSpaceBefore) {
         // Quick description of what we want to do:
         // CAP_MODE_CHARACTERS is always on.
         // CAP_MODE_WORDS is on if there is some whitespace before the cursor.
@@ -230,11 +232,15 @@ public final class StringUtils {
         // single quote since they aren't start punctuation in the unicode sense, but should still
         // be skipped for English. TODO: does this depend on the language?
         int i;
-        for (i = cs.length(); i > 0; i--) {
-            final char c = cs.charAt(i - 1);
-            if (c != Keyboard.CODE_DOUBLE_QUOTE && c != Keyboard.CODE_SINGLE_QUOTE
-                    && Character.getType(c) != Character.START_PUNCTUATION) {
-                break;
+        if (hasSpaceBefore) {
+            i = cs.length() + 1;
+        } else {
+            for (i = cs.length(); i > 0; i--) {
+                final char c = cs.charAt(i - 1);
+                if (c != Keyboard.CODE_DOUBLE_QUOTE && c != Keyboard.CODE_SINGLE_QUOTE
+                        && Character.getType(c) != Character.START_PUNCTUATION) {
+                    break;
+                }
             }
         }
 
@@ -247,6 +253,7 @@ public final class StringUtils {
         // if the first char that's not a space or tab is a start of line (as in, either \n or
         // start of text).
         int j = i;
+        if (hasSpaceBefore) --j;
         while (j > 0 && Character.isWhitespace(cs.charAt(j - 1))) {
             j--;
         }
diff --git a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
index 00cca9d3ba14451ff1554cdb9b9bf8e4532b059e..be3494dc7afad3b51ad36ddf2de219f5e5ec669b 100644
--- a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
@@ -93,22 +93,24 @@ public class StringUtilsTests extends AndroidTestCase {
     }
 
     private void onePathForCaps(final CharSequence cs, final int expectedResult, final int mask,
-            final Locale l) {
+            final Locale l, final boolean hasSpaceBefore) {
         int oneTimeResult = expectedResult & mask;
-        assertEquals("After >" + cs + "<", oneTimeResult, StringUtils.getCapsMode(cs, mask, l));
+        assertEquals("After >" + cs + "<", oneTimeResult,
+                StringUtils.getCapsMode(cs, mask, l, hasSpaceBefore));
     }
 
-    private void allPathsForCaps(final CharSequence cs, final int expectedResult, final Locale l) {
+    private void allPathsForCaps(final CharSequence cs, final int expectedResult, final Locale l,
+            final boolean hasSpaceBefore) {
         final int c = TextUtils.CAP_MODE_CHARACTERS;
         final int w = TextUtils.CAP_MODE_WORDS;
         final int s = TextUtils.CAP_MODE_SENTENCES;
-        onePathForCaps(cs, expectedResult, c | w | s, l);
-        onePathForCaps(cs, expectedResult, w | s, l);
-        onePathForCaps(cs, expectedResult, c | s, l);
-        onePathForCaps(cs, expectedResult, c | w, l);
-        onePathForCaps(cs, expectedResult, c, l);
-        onePathForCaps(cs, expectedResult, w, l);
-        onePathForCaps(cs, expectedResult, s, l);
+        onePathForCaps(cs, expectedResult, c | w | s, l, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, w | s, l, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, c | s, l, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, c | w, l, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, c, l, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, w, l, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, s, l, hasSpaceBefore);
     }
 
     public void testGetCapsMode() {
@@ -116,26 +118,31 @@ public class StringUtilsTests extends AndroidTestCase {
         final int w = TextUtils.CAP_MODE_WORDS;
         final int s = TextUtils.CAP_MODE_SENTENCES;
         Locale l = Locale.ENGLISH;
-        allPathsForCaps("", c | w | s, l);
-        allPathsForCaps("Word", c, l);
-        allPathsForCaps("Word.", c, l);
-        allPathsForCaps("Word ", c | w, l);
-        allPathsForCaps("Word. ", c | w | s, l);
-        allPathsForCaps("Word..", c, l);
-        allPathsForCaps("Word.. ", c | w | s, l);
-        allPathsForCaps("Word... ", c | w | s, l);
-        allPathsForCaps("Word ... ", c | w | s, l);
-        allPathsForCaps("Word . ", c | w, l);
-        allPathsForCaps("In the U.S ", c | w, l);
-        allPathsForCaps("In the U.S. ", c | w, l);
-        allPathsForCaps("Some stuff (e.g. ", c | w, l);
-        allPathsForCaps("In the U.S.. ", c | w | s, l);
-        allPathsForCaps("\"Word.\" ", c | w | s, l);
-        allPathsForCaps("\"Word\". ", c | w | s, l);
-        allPathsForCaps("\"Word\" ", c | w, l);
+        allPathsForCaps("", c | w | s, l, false);
+        allPathsForCaps("Word", c, l, false);
+        allPathsForCaps("Word.", c, l, false);
+        allPathsForCaps("Word ", c | w, l, false);
+        allPathsForCaps("Word. ", c | w | s, l, false);
+        allPathsForCaps("Word..", c, l, false);
+        allPathsForCaps("Word.. ", c | w | s, l, false);
+        allPathsForCaps("Word... ", c | w | s, l, false);
+        allPathsForCaps("Word ... ", c | w | s, l, false);
+        allPathsForCaps("Word . ", c | w, l, false);
+        allPathsForCaps("In the U.S ", c | w, l, false);
+        allPathsForCaps("In the U.S. ", c | w, l, false);
+        allPathsForCaps("Some stuff (e.g. ", c | w, l, false);
+        allPathsForCaps("In the U.S.. ", c | w | s, l, false);
+        allPathsForCaps("\"Word.\" ", c | w | s, l, false);
+        allPathsForCaps("\"Word\". ", c | w | s, l, false);
+        allPathsForCaps("\"Word\" ", c | w, l, false);
+
+        // Test for phantom space
+        allPathsForCaps("Word", c | w, l, true);
+        allPathsForCaps("Word.", c | w | s, l, true);
+
         l = Locale.FRENCH;
-        allPathsForCaps("\"Word.\" ", c | w, l);
-        allPathsForCaps("\"Word\". ", c | w | s, l);
-        allPathsForCaps("\"Word\" ", c | w, l);
+        allPathsForCaps("\"Word.\" ", c | w, l, false);
+        allPathsForCaps("\"Word\". ", c | w | s, l, false);
+        allPathsForCaps("\"Word\" ", c | w, l, false);
     }
 }