diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 20dc9ba53d8a3fe5b0131c3f5e93f95465bcefd3..d590d668557f4a52099c28db0c178c985c120770 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1355,10 +1355,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         }
     }
 
-    private static boolean isAlphabet(final int code) {
-        return Character.isLetter(code);
-    }
-
     private void onSettingsKeyPressed() {
         if (isShowingOptionDialog()) return;
         showSubtypeSelectorAndSettings();
@@ -1989,8 +1985,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         // NOTE: isCursorTouchingWord() is a blocking IPC call, so it often takes several
         // dozen milliseconds. Avoid calling it as much as possible, since we are on the UI
         // thread here.
-        if (!isComposingWord && (isAlphabet(primaryCode)
-                || currentSettings.isWordConnector(primaryCode))
+        if (!isComposingWord && currentSettings.isWordCodePoint(primaryCode)
                 && currentSettings.isSuggestionsRequested(mDisplayOrientation) &&
                 !mConnection.isCursorTouchingWord(currentSettings)) {
             // Reset entirely the composing state anyway, then start composing a new word unless
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index a25cf620c6b72aa930c753dc9d853edfa0818028..195f9f8ef12c59a91b75752f6f18d452ceffef8c 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -22,6 +22,7 @@ import android.content.res.Resources;
 import android.util.Log;
 import android.view.inputmethod.EditorInfo;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.keyboard.internal.KeySpecParser;
 import com.android.inputmethod.latin.Dictionary;
 import com.android.inputmethod.latin.InputAttributes;
@@ -170,6 +171,55 @@ public final class SettingsValues {
         mIsInternal = Settings.isInternal(prefs);
     }
 
+    // Only for tests
+    private SettingsValues(final Locale locale) {
+        // TODO: locale is saved, but not used yet. May have to change this if tests require.
+        mLocale = locale;
+        mDelayUpdateOldSuggestions = 0;
+        mSymbolsPrecededBySpace = new int[] { '(', '[', '{', '&' };
+        Arrays.sort(mSymbolsPrecededBySpace);
+        mSymbolsFollowedBySpace = new int[] { '.', ',', ';', ':', '!', '?', ')', ']', '}', '&' };
+        Arrays.sort(mSymbolsFollowedBySpace);
+        mWordConnectors = new int[] { '\'', '-' };
+        Arrays.sort(mWordConnectors);
+        final String[] suggestPuncsSpec = new String[] { "!", "?", ",", ":", ";" };
+        mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec);
+        mWordSeparators = "&\t \n()[]{}*&<>+=|.,;:!?/_\"";
+        mHintToSaveText = "Touch again to save";
+        mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
+        mAutoCap = true;
+        mVibrateOn = true;
+        mSoundOn = true;
+        mKeyPreviewPopupOn = true;
+        mSlidingKeyInputPreviewEnabled = true;
+        mVoiceMode = "0";
+        mIncludesOtherImesInLanguageSwitchList = false;
+        mShowsLanguageSwitchKey = true;
+        mUseContactsDict = true;
+        mUseDoubleSpacePeriod = true;
+        mBlockPotentiallyOffensive = true;
+        mAutoCorrectEnabled = true;
+        mBigramPredictionEnabled = true;
+        mKeyLongpressTimeout = 300;
+        mKeypressVibrationDuration = 5;
+        mKeypressSoundVolume = 1;
+        mKeyPreviewPopupDismissDelay = 70;
+        mAutoCorrectionThreshold = 1;
+        mVoiceKeyEnabled = true;
+        mVoiceKeyOnMain = true;
+        mGestureInputEnabled = true;
+        mGestureTrailEnabled = true;
+        mGestureFloatingPreviewTextEnabled = true;
+        mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect;
+        mSuggestionVisibility = 0;
+        mIsInternal = false;
+    }
+
+    @UsedForTesting
+    public static SettingsValues makeDummySettingsValuesForTest(final Locale locale) {
+        return new SettingsValues(locale);
+    }
+
     public boolean isApplicationSpecifiedCompletionsOn() {
         return mInputAttributes.mApplicationSpecifiedCompletionOn;
     }
@@ -194,6 +244,10 @@ public final class SettingsValues {
         return Arrays.binarySearch(mWordConnectors, code) >= 0;
     }
 
+    public boolean isWordCodePoint(final int code) {
+        return Character.isLetter(code) || isWordConnector(code);
+    }
+
     public boolean isUsuallyPrecededBySpace(final int code) {
         return Arrays.binarySearch(mSymbolsPrecededBySpace, code) >= 0;
     }
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index 7406d855a3cbc50b1bf34295bdb34bf13076f473..f88f2cca7ddb91ef04e3cd03480ccc6f8db94db6 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.latin.utils;
 import android.text.TextUtils;
 
 import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.settings.SettingsValues;
 
 import java.util.ArrayList;
 import java.util.Locale;
@@ -193,27 +194,55 @@ public final class StringUtils {
     }
 
     public static boolean isIdenticalAfterUpcase(final String text) {
-        final int len = text.length();
-        for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) {
+        final int length = text.length();
+        int i = 0;
+        while (i < length) {
             final int codePoint = text.codePointAt(i);
             if (Character.isLetter(codePoint) && !Character.isUpperCase(codePoint)) {
                 return false;
             }
+            i += Character.charCount(codePoint);
         }
         return true;
     }
 
     public static boolean isIdenticalAfterDowncase(final String text) {
-        final int len = text.length();
-        for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) {
+        final int length = text.length();
+        int i = 0;
+        while (i < length) {
             final int codePoint = text.codePointAt(i);
             if (Character.isLetter(codePoint) && !Character.isLowerCase(codePoint)) {
                 return false;
             }
+            i += Character.charCount(codePoint);
         }
         return true;
     }
 
+    public static boolean looksValidForDictionaryInsertion(final CharSequence text,
+            final SettingsValues settings) {
+        if (TextUtils.isEmpty(text)) return false;
+        final int length = text.length();
+        int i = 0;
+        int digitCount = 0;
+        while (i < length) {
+            final int codePoint = Character.codePointAt(text, i);
+            final int charCount = Character.charCount(codePoint);
+            i += charCount;
+            if (Character.isDigit(codePoint)) {
+                // Count digits: see below
+                digitCount += charCount;
+                continue;
+            }
+            if (!settings.isWordCodePoint(codePoint)) return false;
+        }
+        // We reject strings entirely comprised of digits to avoid using PIN codes or credit
+        // card numbers. It would come in handy for word prediction though; a good example is
+        // when writing one's address where the street number is usually quite discriminative,
+        // as well as the postal code.
+        return digitCount < length;
+    }
+
     public static boolean isIdenticalAfterCapitalizeEachWord(final String text,
             final String separators) {
         boolean needCapsNext = true;
diff --git a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
index 9ee8e387b3baacf57e9b7825b1f0edf995c07b7b..175e511b0f816c3b5f8a04567073fdb06c9b7a8d 100644
--- a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin.utils;
 
+import com.android.inputmethod.latin.settings.SettingsValues;
+
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -183,6 +185,18 @@ public class StringUtilsTests extends AndroidTestCase {
         assertTrue(StringUtils.isIdenticalAfterDowncase(""));
     }
 
+    public void testLooksValidForDictionaryInsertion() {
+        final SettingsValues settings =
+                SettingsValues.makeDummySettingsValuesForTest(Locale.ENGLISH);
+        assertTrue(StringUtils.looksValidForDictionaryInsertion("aochaueo", settings));
+        assertFalse(StringUtils.looksValidForDictionaryInsertion("", settings));
+        assertTrue(StringUtils.looksValidForDictionaryInsertion("ao-ch'aueo", settings));
+        assertFalse(StringUtils.looksValidForDictionaryInsertion("2908743256", settings));
+        assertTrue(StringUtils.looksValidForDictionaryInsertion("31aochaueo", settings));
+        assertFalse(StringUtils.looksValidForDictionaryInsertion("akeo  raeoch oerch .", settings));
+        assertFalse(StringUtils.looksValidForDictionaryInsertion("!!!", settings));
+    }
+
     private static void checkCapitalize(final String src, final String dst, final String separators,
             final Locale locale) {
         assertEquals(dst, StringUtils.capitalizeEachWord(src, separators, locale));