diff --git a/java/src/com/android/inputmethod/latin/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/CapsModeUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..1012cd519c9e7ea09a29f5ab19d482d1cfa5b3e8
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/CapsModeUtils.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.text.InputType;
+import android.text.TextUtils;
+
+import java.util.Locale;
+
+public final class CapsModeUtils {
+    private CapsModeUtils() {
+        // This utility class is not publicly instantiable.
+    }
+
+    /**
+     * Apply an auto-caps mode to a string.
+     *
+     * This intentionally does NOT apply manual caps mode. It only changes the capitalization if
+     * the mode is one of the auto-caps modes.
+     * @param s The string to capitalize.
+     * @param capitalizeMode The mode in which to capitalize.
+     * @param locale The locale for capitalizing.
+     * @return The capitalized string.
+     */
+    public static String applyAutoCapsMode(final String s, final int capitalizeMode,
+            final Locale locale) {
+        if (WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED == capitalizeMode) {
+            return s.toUpperCase(locale);
+        } else if (WordComposer.CAPS_MODE_AUTO_SHIFTED == capitalizeMode) {
+            return StringUtils.toTitleCase(s, locale);
+        } else {
+            return s;
+        }
+    }
+
+    /**
+     * Return whether a constant represents an auto-caps mode (either auto-shift or auto-shift-lock)
+     * @param mode The mode to test for
+     * @return true if this represents an auto-caps mode, false otherwise
+     */
+    public static boolean isAutoCapsMode(final int mode) {
+        return WordComposer.CAPS_MODE_AUTO_SHIFTED == mode
+                || WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED == mode;
+    }
+
+    /**
+     * Determine what caps mode should be in effect at the current offset in
+     * the text. Only the mode bits set in <var>reqModes</var> will be
+     * checked. Note that the caps mode flags here are explicitly defined
+     * to match those in {@link InputType}.
+     *
+     * This code is a straight copy of TextUtils.getCapsMode (modulo namespace and formatting
+     * issues). This will change in the future as we simplify the code for our use and fix bugs.
+     *
+     * @param cs The text that should be checked for caps modes.
+     * @param reqModes The modes to be checked: may be any combination of
+     * {@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,
+            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.
+        // CAP_MODE_SENTENCES is on if there is some whitespace before the cursor, and the end
+        //   of a sentence just before that.
+        // We ignore opening parentheses and the like just before the cursor for purposes of
+        // finding whitespace for WORDS and SENTENCES modes.
+        // The end of a sentence ends with a period, question mark or exclamation mark. If it's
+        // a period, it also needs not to be an abbreviation, which means it also needs to either
+        // be immediately preceded by punctuation, or by a string of only letters with single
+        // periods interleaved.
+
+        // Step 1 : check for cap MODE_CHARACTERS. If it's looked for, it's always on.
+        if ((reqModes & (TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES)) == 0) {
+            // Here we are not looking for MODE_WORDS or MODE_SENTENCES, so since we already
+            // evaluated MODE_CHARACTERS, we can return.
+            return TextUtils.CAP_MODE_CHARACTERS & reqModes;
+        }
+
+        // Step 2 : Skip (ignore at the end of input) any opening punctuation. This includes
+        // opening parentheses, brackets, opening quotes, everything that *opens* a span of
+        // text in the linguistic sense. In RTL languages, this is still an opening sign, although
+        // it may look like a right parenthesis for example. We also include double quote and
+        // 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;
+        if (hasSpaceBefore) {
+            i = cs.length() + 1;
+        } else {
+            for (i = cs.length(); i > 0; i--) {
+                final char c = cs.charAt(i - 1);
+                if (c != Constants.CODE_DOUBLE_QUOTE && c != Constants.CODE_SINGLE_QUOTE
+                        && Character.getType(c) != Character.START_PUNCTUATION) {
+                    break;
+                }
+            }
+        }
+
+        // We are now on the character that precedes any starting punctuation, so in the most
+        // frequent case this will be whitespace or a letter, although it may occasionally be a
+        // start of line, or some symbol.
+
+        // Step 3 : Search for the start of a paragraph. From the starting point computed in step 2,
+        // we go back over any space or tab char sitting there. We find the start of a paragraph
+        // if the first char that's not a space or tab is a start of line (as in \n, start of text,
+        // or some other similar characters).
+        int j = i;
+        char prevChar = Constants.CODE_SPACE;
+        if (hasSpaceBefore) --j;
+        while (j > 0) {
+            prevChar = cs.charAt(j - 1);
+            if (!Character.isSpaceChar(prevChar) && prevChar != Constants.CODE_TAB) break;
+            j--;
+        }
+        if (j <= 0 || Character.isWhitespace(prevChar)) {
+            // There are only spacing chars between the start of the paragraph and the cursor,
+            // defined as a isWhitespace() char that is neither a isSpaceChar() nor a tab. Both
+            // MODE_WORDS and MODE_SENTENCES should be active.
+            return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS
+                    | TextUtils.CAP_MODE_SENTENCES) & reqModes;
+        }
+        if (i == j) {
+            // If we don't have whitespace before index i, it means neither MODE_WORDS
+            // nor mode sentences should be on so we can return right away.
+            return TextUtils.CAP_MODE_CHARACTERS & reqModes;
+        }
+        if ((reqModes & TextUtils.CAP_MODE_SENTENCES) == 0) {
+            // Here we know we have whitespace before the cursor (if not, we returned in the above
+            // if i == j clause), so we need MODE_WORDS to be on. And we don't need to evaluate
+            // MODE_SENTENCES so we can return right away.
+            return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
+        }
+        // Please note that because of the reqModes & CAP_MODE_SENTENCES test a few lines above,
+        // we know that MODE_SENTENCES is being requested.
+
+        // Step 4 : Search for MODE_SENTENCES.
+        // English is a special case in that "American typography" rules, which are the most common
+        // in English, state that a sentence terminator immediately following a quotation mark
+        // should be swapped with it and de-duplicated (included in the quotation mark),
+        // e.g. <<Did he say, "let's go home?">>
+        // No other language has such a rule as far as I know, instead putting inside the quotation
+        // mark as the exact thing quoted and handling the surrounding punctuation independently,
+        // e.g. <<Did he say, "let's go home"?>>
+        // Hence, specifically for English, we treat this special case here.
+        if (Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) {
+            for (; j > 0; j--) {
+                // Here we look to go over any closing punctuation. This is because in dominant
+                // variants of English, the final period is placed within double quotes and maybe
+                // other closing punctuation signs. This is generally not true in other languages.
+                final char c = cs.charAt(j - 1);
+                if (c != Constants.CODE_DOUBLE_QUOTE && c != Constants.CODE_SINGLE_QUOTE
+                        && Character.getType(c) != Character.END_PUNCTUATION) {
+                    break;
+                }
+            }
+        }
+
+        if (j <= 0) return TextUtils.CAP_MODE_CHARACTERS & reqModes;
+        char c = cs.charAt(--j);
+
+        // We found the next interesting chunk of text ; next we need to determine if it's the
+        // end of a sentence. If we have a question mark or an exclamation mark, it's the end of
+        // a sentence. If it's neither, the only remaining case is the period so we get the opposite
+        // case out of the way.
+        if (c == Constants.CODE_QUESTION_MARK || c == Constants.CODE_EXCLAMATION_MARK) {
+            return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_SENTENCES) & reqModes;
+        }
+        if (c != Constants.CODE_PERIOD || j <= 0) {
+            return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
+        }
+
+        // We found out that we have a period. We need to determine if this is a full stop or
+        // otherwise sentence-ending period, or an abbreviation like "e.g.". An abbreviation
+        // looks like (\w\.){2,}
+        // To find out, we will have a simple state machine with the following states :
+        // START, WORD, PERIOD, ABBREVIATION
+        // On START : (just before the first period)
+        //           letter => WORD
+        //           whitespace => end with no caps (it was a stand-alone period)
+        //           otherwise => end with caps (several periods/symbols in a row)
+        // On WORD : (within the word just before the first period)
+        //           letter => WORD
+        //           period => PERIOD
+        //           otherwise => end with caps (it was a word with a full stop at the end)
+        // On PERIOD : (period within a potential abbreviation)
+        //           letter => LETTER
+        //           otherwise => end with caps (it was not an abbreviation)
+        // On LETTER : (letter within a potential abbreviation)
+        //           letter => LETTER
+        //           period => PERIOD
+        //           otherwise => end with no caps (it was an abbreviation)
+        // "Not an abbreviation" in the above chart essentially covers cases like "...yes.". This
+        // should capitalize.
+
+        final int START = 0;
+        final int WORD = 1;
+        final int PERIOD = 2;
+        final int LETTER = 3;
+        final int caps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS
+                | TextUtils.CAP_MODE_SENTENCES) & reqModes;
+        final int noCaps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
+        int state = START;
+        while (j > 0) {
+            c = cs.charAt(--j);
+            switch (state) {
+            case START:
+                if (Character.isLetter(c)) {
+                    state = WORD;
+                } else if (Character.isWhitespace(c)) {
+                    return noCaps;
+                } else {
+                    return caps;
+                }
+                break;
+            case WORD:
+                if (Character.isLetter(c)) {
+                    state = WORD;
+                } else if (c == Constants.CODE_PERIOD) {
+                    state = PERIOD;
+                } else {
+                    return caps;
+                }
+                break;
+            case PERIOD:
+                if (Character.isLetter(c)) {
+                    state = LETTER;
+                } else {
+                    return caps;
+                }
+                break;
+            case LETTER:
+                if (Character.isLetter(c)) {
+                    state = LETTER;
+                } else if (c == Constants.CODE_PERIOD) {
+                    state = PERIOD;
+                } else {
+                    return noCaps;
+                }
+            }
+        }
+        // Here we arrived at the start of the line. This should behave exactly like whitespace.
+        return (START == state || LETTER == state) ? noCaps : caps;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 1c49bb0cc9378b86882a751b81c9b8ed05f80f16..73ace2bfa046ef574d5587e13eeb3772380cb90f 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1228,7 +1228,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
             return;
         }
         final String wordToEdit;
-        if (StringUtils.isAutoCapsMode(mLastComposedWord.mCapitalizedMode)) {
+        if (CapsModeUtils.isAutoCapsMode(mLastComposedWord.mCapitalizedMode)) {
             wordToEdit = word.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
         } else {
             wordToEdit = word;
diff --git a/java/src/com/android/inputmethod/latin/PositionalInfoForUserDictPendingAddition.java b/java/src/com/android/inputmethod/latin/PositionalInfoForUserDictPendingAddition.java
index 9fdbf8703c22d3f1ad92eb5ea082447db1989245..a8800007a68b849675481f5205d7531834b97bf1 100644
--- a/java/src/com/android/inputmethod/latin/PositionalInfoForUserDictPendingAddition.java
+++ b/java/src/com/android/inputmethod/latin/PositionalInfoForUserDictPendingAddition.java
@@ -96,7 +96,7 @@ public final class PositionalInfoForUserDictPendingAddition {
         if (currentCursorPosition != mCursorPos) return true;
         // We have made all the checks : do the replacement and report success
         // If this was auto-capitalized, we need to restore the case before committing
-        final String wordWithCaseFixed = StringUtils.applyAutoCapsMode(mActualWordBeingAdded,
+        final String wordWithCaseFixed = CapsModeUtils.applyAutoCapsMode(mActualWordBeingAdded,
                 mCapitalizedMode, locale);
         connection.setComposingRegion(currentCursorPosition - mOriginalWord.length(),
                 currentCursorPosition);
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 521cea9777fae73acb4de01d4ff4903a69ffb4fa..7300dbd23cc1e8c498d113ad3d84c36cc7494298 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -221,7 +221,7 @@ public final 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 CapsModeUtils.getCapsMode(mCommittedTextBeforeComposingText, inputType, locale,
                 hasSpaceBefore);
     }
 
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index 6fac0d2d34f1b72f2f9899d4318c97e320ffe24c..90c3fcdd251f048387fcf7d534a4f8aa858fbac6 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -16,7 +16,6 @@
 
 package com.android.inputmethod.latin;
 
-import android.text.InputType;
 import android.text.TextUtils;
 
 import java.util.ArrayList;
@@ -103,37 +102,6 @@ public final class StringUtils {
         }
     }
 
-    /**
-     * Apply an auto-caps mode to a string.
-     *
-     * This intentionally does NOT apply manual caps mode. It only changes the capitalization if
-     * the mode is one of the auto-caps modes.
-     * @param s The string to capitalize.
-     * @param capitalizeMode The mode in which to capitalize.
-     * @param locale The locale for capitalizing.
-     * @return The capitalized string.
-     */
-    public static String applyAutoCapsMode(final String s, final int capitalizeMode,
-            final Locale locale) {
-        if (WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED == capitalizeMode) {
-            return s.toUpperCase(locale);
-        } else if (WordComposer.CAPS_MODE_AUTO_SHIFTED == capitalizeMode) {
-            return toTitleCase(s, locale);
-        } else {
-            return s;
-        }
-    }
-
-    /**
-     * Return whether a constant represents an auto-caps mode (either auto-shift or auto-shift-lock)
-     * @param mode The mode to test for
-     * @return true if this represents an auto-caps mode, false otherwise
-     */
-    public static boolean isAutoCapsMode(final int mode) {
-        return WordComposer.CAPS_MODE_AUTO_SHIFTED == mode
-                || WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED == mode;
-    }
-
     public static String toTitleCase(final String s, final Locale locale) {
         if (s.length() <= 1) {
             // TODO: is this really correct? Shouldn't this be s.toUpperCase()?
@@ -166,213 +134,6 @@ public final class StringUtils {
         return codePoints;
     }
 
-    /**
-     * Determine what caps mode should be in effect at the current offset in
-     * the text. Only the mode bits set in <var>reqModes</var> will be
-     * checked. Note that the caps mode flags here are explicitly defined
-     * to match those in {@link InputType}.
-     *
-     * This code is a straight copy of TextUtils.getCapsMode (modulo namespace and formatting
-     * issues). This will change in the future as we simplify the code for our use and fix bugs.
-     *
-     * @param cs The text that should be checked for caps modes.
-     * @param reqModes The modes to be checked: may be any combination of
-     * {@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,
-            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.
-        // CAP_MODE_SENTENCES is on if there is some whitespace before the cursor, and the end
-        //   of a sentence just before that.
-        // We ignore opening parentheses and the like just before the cursor for purposes of
-        // finding whitespace for WORDS and SENTENCES modes.
-        // The end of a sentence ends with a period, question mark or exclamation mark. If it's
-        // a period, it also needs not to be an abbreviation, which means it also needs to either
-        // be immediately preceded by punctuation, or by a string of only letters with single
-        // periods interleaved.
-
-        // Step 1 : check for cap MODE_CHARACTERS. If it's looked for, it's always on.
-        if ((reqModes & (TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES)) == 0) {
-            // Here we are not looking for MODE_WORDS or MODE_SENTENCES, so since we already
-            // evaluated MODE_CHARACTERS, we can return.
-            return TextUtils.CAP_MODE_CHARACTERS & reqModes;
-        }
-
-        // Step 2 : Skip (ignore at the end of input) any opening punctuation. This includes
-        // opening parentheses, brackets, opening quotes, everything that *opens* a span of
-        // text in the linguistic sense. In RTL languages, this is still an opening sign, although
-        // it may look like a right parenthesis for example. We also include double quote and
-        // 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;
-        if (hasSpaceBefore) {
-            i = cs.length() + 1;
-        } else {
-            for (i = cs.length(); i > 0; i--) {
-                final char c = cs.charAt(i - 1);
-                if (c != Constants.CODE_DOUBLE_QUOTE && c != Constants.CODE_SINGLE_QUOTE
-                        && Character.getType(c) != Character.START_PUNCTUATION) {
-                    break;
-                }
-            }
-        }
-
-        // We are now on the character that precedes any starting punctuation, so in the most
-        // frequent case this will be whitespace or a letter, although it may occasionally be a
-        // start of line, or some symbol.
-
-        // Step 3 : Search for the start of a paragraph. From the starting point computed in step 2,
-        // we go back over any space or tab char sitting there. We find the start of a paragraph
-        // if the first char that's not a space or tab is a start of line (as in \n, start of text,
-        // or some other similar characters).
-        int j = i;
-        char prevChar = Constants.CODE_SPACE;
-        if (hasSpaceBefore) --j;
-        while (j > 0) {
-            prevChar = cs.charAt(j - 1);
-            if (!Character.isSpaceChar(prevChar) && prevChar != Constants.CODE_TAB) break;
-            j--;
-        }
-        if (j <= 0 || Character.isWhitespace(prevChar)) {
-            // There are only spacing chars between the start of the paragraph and the cursor,
-            // defined as a isWhitespace() char that is neither a isSpaceChar() nor a tab. Both
-            // MODE_WORDS and MODE_SENTENCES should be active.
-            return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS
-                    | TextUtils.CAP_MODE_SENTENCES) & reqModes;
-        }
-        if (i == j) {
-            // If we don't have whitespace before index i, it means neither MODE_WORDS
-            // nor mode sentences should be on so we can return right away.
-            return TextUtils.CAP_MODE_CHARACTERS & reqModes;
-        }
-        if ((reqModes & TextUtils.CAP_MODE_SENTENCES) == 0) {
-            // Here we know we have whitespace before the cursor (if not, we returned in the above
-            // if i == j clause), so we need MODE_WORDS to be on. And we don't need to evaluate
-            // MODE_SENTENCES so we can return right away.
-            return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
-        }
-        // Please note that because of the reqModes & CAP_MODE_SENTENCES test a few lines above,
-        // we know that MODE_SENTENCES is being requested.
-
-        // Step 4 : Search for MODE_SENTENCES.
-        // English is a special case in that "American typography" rules, which are the most common
-        // in English, state that a sentence terminator immediately following a quotation mark
-        // should be swapped with it and de-duplicated (included in the quotation mark),
-        // e.g. <<Did he say, "let's go home?">>
-        // No other language has such a rule as far as I know, instead putting inside the quotation
-        // mark as the exact thing quoted and handling the surrounding punctuation independently,
-        // e.g. <<Did he say, "let's go home"?>>
-        // Hence, specifically for English, we treat this special case here.
-        if (Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) {
-            for (; j > 0; j--) {
-                // Here we look to go over any closing punctuation. This is because in dominant
-                // variants of English, the final period is placed within double quotes and maybe
-                // other closing punctuation signs. This is generally not true in other languages.
-                final char c = cs.charAt(j - 1);
-                if (c != Constants.CODE_DOUBLE_QUOTE && c != Constants.CODE_SINGLE_QUOTE
-                        && Character.getType(c) != Character.END_PUNCTUATION) {
-                    break;
-                }
-            }
-        }
-
-        if (j <= 0) return TextUtils.CAP_MODE_CHARACTERS & reqModes;
-        char c = cs.charAt(--j);
-
-        // We found the next interesting chunk of text ; next we need to determine if it's the
-        // end of a sentence. If we have a question mark or an exclamation mark, it's the end of
-        // a sentence. If it's neither, the only remaining case is the period so we get the opposite
-        // case out of the way.
-        if (c == Constants.CODE_QUESTION_MARK || c == Constants.CODE_EXCLAMATION_MARK) {
-            return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_SENTENCES) & reqModes;
-        }
-        if (c != Constants.CODE_PERIOD || j <= 0) {
-            return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
-        }
-
-        // We found out that we have a period. We need to determine if this is a full stop or
-        // otherwise sentence-ending period, or an abbreviation like "e.g.". An abbreviation
-        // looks like (\w\.){2,}
-        // To find out, we will have a simple state machine with the following states :
-        // START, WORD, PERIOD, ABBREVIATION
-        // On START : (just before the first period)
-        //           letter => WORD
-        //           whitespace => end with no caps (it was a stand-alone period)
-        //           otherwise => end with caps (several periods/symbols in a row)
-        // On WORD : (within the word just before the first period)
-        //           letter => WORD
-        //           period => PERIOD
-        //           otherwise => end with caps (it was a word with a full stop at the end)
-        // On PERIOD : (period within a potential abbreviation)
-        //           letter => LETTER
-        //           otherwise => end with caps (it was not an abbreviation)
-        // On LETTER : (letter within a potential abbreviation)
-        //           letter => LETTER
-        //           period => PERIOD
-        //           otherwise => end with no caps (it was an abbreviation)
-        // "Not an abbreviation" in the above chart essentially covers cases like "...yes.". This
-        // should capitalize.
-
-        final int START = 0;
-        final int WORD = 1;
-        final int PERIOD = 2;
-        final int LETTER = 3;
-        final int caps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS
-                | TextUtils.CAP_MODE_SENTENCES) & reqModes;
-        final int noCaps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
-        int state = START;
-        while (j > 0) {
-            c = cs.charAt(--j);
-            switch (state) {
-            case START:
-                if (Character.isLetter(c)) {
-                    state = WORD;
-                } else if (Character.isWhitespace(c)) {
-                    return noCaps;
-                } else {
-                    return caps;
-                }
-                break;
-            case WORD:
-                if (Character.isLetter(c)) {
-                    state = WORD;
-                } else if (c == Constants.CODE_PERIOD) {
-                    state = PERIOD;
-                } else {
-                    return caps;
-                }
-                break;
-            case PERIOD:
-                if (Character.isLetter(c)) {
-                    state = LETTER;
-                } else {
-                    return caps;
-                }
-                break;
-            case LETTER:
-                if (Character.isLetter(c)) {
-                    state = LETTER;
-                } else if (c == Constants.CODE_PERIOD) {
-                    state = PERIOD;
-                } else {
-                    return noCaps;
-                }
-            }
-        }
-        // Here we arrived at the start of the line. This should behave exactly like whitespace.
-        return (START == state || LETTER == state) ? noCaps : caps;
-    }
-
     public static String[] parseCsvString(final String text) {
         final int size = text.length();
         if (size == 0) {
diff --git a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java
index 49ef3a2619d7d6241e0e4af278e97d87038ad4a6..8fef1de2a85fb6365d92dce79cbd4cd36346d525 100644
--- a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.keyboard;
diff --git a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java
index 93883dc5b613d6fe8fb403709fb98d976fc73688..ee4c72a85cc1c9b5beced160a356ac60abde1719 100644
--- a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2011 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.keyboard;
diff --git a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java b/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java
index d0426eb72c1d762072797482336f9a9f22929bd1..1398db97c1c97835633dc39fff31b938a6d3bea4 100644
--- a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.keyboard;
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java
index d58e1627fc9a2692fdc24e3195c5b4faffd2ef57..05f39551b414605a93730f9351721b54453e6b46 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2010 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.keyboard.internal;
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
index a5b61b370222e97799663295a8dbfb9ddb9d7998..b1ae6f5b7aa0186353b5dff280078bea400905fe 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2010 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.keyboard.internal;
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java
index 5fbe6d4bf35e24e06cfc136f7061b2deb751fafc..b193e66dc92256ad5eca819e1e8024940251ea7b 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.keyboard.internal;
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java
index 637d47577f474c39dbc33016f531ab589e1ff959..df9ce5e315c2f5ad0d0feb6344e59878107657da 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.keyboard.internal;
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java
index 08199a0743a804be01b1d18d66ac4c67e963a69e..5e94aebf61c58ff7c1c21e0bfee5abc6a2c59990 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.keyboard.internal;
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
index ac3558521df904e1014d35162df5e5d634876d61..eb484084e3727b0703f5cc77520520e7214b4c45 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.keyboard.internal;
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java b/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java
index e7068afce9152f774820e893012f959a41c98722..d6700cfb5bcbf04549a8ff5a8620b06d0ea94250 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.keyboard.internal;
diff --git a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
index 594b5d6b24a0d474ed91dfa40c70ae4c0f1744b0..7275d3afad598d9251f3358f8e08ada2b09cc492 100644
--- a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
+++ b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.latin;
diff --git a/tests/src/com/android/inputmethod/latin/CapsModeUtilsTests.java b/tests/src/com/android/inputmethod/latin/CapsModeUtilsTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..339791d5779e7c320b909c6fc9d707046c1afa92
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/CapsModeUtilsTests.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
+
+import java.util.Locale;
+
+@SmallTest
+public class CapsModeUtilsTests extends AndroidTestCase {
+    private static void onePathForCaps(final CharSequence cs, final int expectedResult,
+            final int mask, final Locale l, final boolean hasSpaceBefore) {
+        int oneTimeResult = expectedResult & mask;
+        assertEquals("After >" + cs + "<", oneTimeResult,
+                CapsModeUtils.getCapsMode(cs, mask, l, hasSpaceBefore));
+    }
+
+    private static 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, 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() {
+        final int c = TextUtils.CAP_MODE_CHARACTERS;
+        final int w = TextUtils.CAP_MODE_WORDS;
+        final int s = TextUtils.CAP_MODE_SENTENCES;
+        Locale l = Locale.ENGLISH;
+        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);
+
+        // Tests after some whitespace
+        allPathsForCaps("Word\n", c | w | s, l, false);
+        allPathsForCaps("Word\n", c | w | s, l, true);
+        allPathsForCaps("Word\n ", c | w | s, l, true);
+        allPathsForCaps("Word.\n", c | w | s, l, false);
+        allPathsForCaps("Word.\n", c | w | s, l, true);
+        allPathsForCaps("Word.\n ", c | w | s, l, true);
+
+        l = Locale.FRENCH;
+        allPathsForCaps("\"Word.\" ", c | w, l, false);
+        allPathsForCaps("\"Word\". ", c | w | s, l, false);
+        allPathsForCaps("\"Word\" ", c | w, l, false);
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java
index fd584761a27332c7b3fd1136df7c6de6d54cee29..65dfd2dde46dde4caea3225cf57ba8742f791176 100644
--- a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.latin;
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index d7b2407803f2e81649a19a76c4c1e5402bb49275..72c8d9c2ff3f998d20afbaeb60d73d490daecc03 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.latin;
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
index e012f79263e4529483f1426dccfcf67430126967..005f8b279649ba937bee927fc97137113049ad3e 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.latin;
diff --git a/tests/src/com/android/inputmethod/latin/InputPointersTests.java b/tests/src/com/android/inputmethod/latin/InputPointersTests.java
index 44526a37c9bba0cdcdf88055d3db5530b79d681e..e1149b3fd5942182097b12cb514531da6dc1f4c0 100644
--- a/tests/src/com/android/inputmethod/latin/InputPointersTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputPointersTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.latin;
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index c4405877217f916253af96f15eccfebf203d201e..4ccbf48574e9d15e6123b7f22d0e4f5985837c90 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.latin;
diff --git a/tests/src/com/android/inputmethod/latin/PunctuationTests.java b/tests/src/com/android/inputmethod/latin/PunctuationTests.java
index 38203e8fcb80095638280e1b9c3aa1677cf5f97e..1b2f0e679eb69bd7637dc25505e5c714ac7cb827 100644
--- a/tests/src/com/android/inputmethod/latin/PunctuationTests.java
+++ b/tests/src/com/android/inputmethod/latin/PunctuationTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.latin;
diff --git a/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java b/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java
index 7c415dff088ea091f9c20d37f27fb6159d7a2144..2d1b836c22ebe4f9323cd8e858be943dfa77cc4d 100644
--- a/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java
+++ b/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.latin;
diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java
index 9b8fc6d5dbe51c9743e72aab1a987d9da4c1dc39..9e545a5b206c35fcbd0df3198ba877a0d590679a 100644
--- a/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java
+++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java
@@ -1,17 +1,17 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.latin;
diff --git a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
index f3fbb3e4b0e3bdc0670a2249c7cb43378eef072b..923ab2ecc5971672d8c19f55cc0056834f090e6e 100644
--- a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
@@ -1,26 +1,23 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.latin;
 
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.text.TextUtils;
-
-import java.util.Locale;
 
 @SmallTest
 public class StringUtilsTests extends AndroidTestCase {
@@ -93,66 +90,4 @@ public class StringUtilsTests extends AndroidTestCase {
         assertEquals("in 5 elements at position 2,4", "key1,key3,key5",
                 StringUtils.removeFromCsvIfExists("key", "key1,key,key3,key,key5"));
     }
-
-    private void onePathForCaps(final CharSequence cs, final int expectedResult, final int mask,
-            final Locale l, final boolean hasSpaceBefore) {
-        int oneTimeResult = expectedResult & mask;
-        assertEquals("After >" + cs + "<", oneTimeResult,
-                StringUtils.getCapsMode(cs, mask, l, hasSpaceBefore));
-    }
-
-    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, 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() {
-        final int c = TextUtils.CAP_MODE_CHARACTERS;
-        final int w = TextUtils.CAP_MODE_WORDS;
-        final int s = TextUtils.CAP_MODE_SENTENCES;
-        Locale l = Locale.ENGLISH;
-        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);
-
-        // Tests after some whitespace
-        allPathsForCaps("Word\n", c | w | s, l, false);
-        allPathsForCaps("Word\n", c | w | s, l, true);
-        allPathsForCaps("Word\n ", c | w | s, l, true);
-        allPathsForCaps("Word.\n", c | w | s, l, false);
-        allPathsForCaps("Word.\n", c | w | s, l, true);
-        allPathsForCaps("Word.\n ", c | w | s, l, true);
-
-        l = Locale.FRENCH;
-        allPathsForCaps("\"Word.\" ", c | w, l, false);
-        allPathsForCaps("\"Word\". ", c | w | s, l, false);
-        allPathsForCaps("\"Word\" ", c | w, l, false);
-    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
index e406e84af53900a7304fbd393336a37df6003fef..1e20366624e64599a34495f8b5b227f932dfab48 100644
--- a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
+++ b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2011 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.latin;
diff --git a/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java b/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java
index 1ea6b2e98f2eb2e784ae23e4e1dd976facd26178..879cc466414180d889708847208cae3abab4d174 100644
--- a/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java
+++ b/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.latin.spellcheck;
diff --git a/tests/src/com/android/inputmethod/research/MotionEventReaderTests.java b/tests/src/com/android/inputmethod/research/MotionEventReaderTests.java
index e5b9bc0ce05da21bae4a6984821caf57aff625c6..28a9f3d5c677c62dea0c58be6fd9482d9a9f31cd 100644
--- a/tests/src/com/android/inputmethod/research/MotionEventReaderTests.java
+++ b/tests/src/com/android/inputmethod/research/MotionEventReaderTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2013 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.research;