Merge "Update dictionaries"
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 7dda0a6..3206413 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -496,6 +496,8 @@
             <enum name="greek" value="2" />
             <enum name="arabic" value="3" />
             <enum name="hebrew" value="4" />
+            <enum name="armenian" value="5" />
+            <enum name="georgian" value="6" />
         </attr>
     </declare-styleable>
 
diff --git a/java/res/xml/keyboard_layout_set_armenian_phonetic.xml b/java/res/xml/keyboard_layout_set_armenian_phonetic.xml
index 35bd43f..c3a1189 100644
--- a/java/res/xml/keyboard_layout_set_armenian_phonetic.xml
+++ b/java/res/xml/keyboard_layout_set_armenian_phonetic.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="armenian" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_armenian_phonetic"
diff --git a/java/res/xml/keyboard_layout_set_bulgarian.xml b/java/res/xml/keyboard_layout_set_bulgarian.xml
index c6fdff9..3f53865 100644
--- a/java/res/xml/keyboard_layout_set_bulgarian.xml
+++ b/java/res/xml/keyboard_layout_set_bulgarian.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="cyrillic" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_bulgarian"
diff --git a/java/res/xml/keyboard_layout_set_bulgarian_bds.xml b/java/res/xml/keyboard_layout_set_bulgarian_bds.xml
index a36b3bd..8e92f70 100644
--- a/java/res/xml/keyboard_layout_set_bulgarian_bds.xml
+++ b/java/res/xml/keyboard_layout_set_bulgarian_bds.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="cyrillic" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_bulgarian_bds"
diff --git a/java/res/xml/keyboard_layout_set_east_slavic.xml b/java/res/xml/keyboard_layout_set_east_slavic.xml
index 8d66faf..ef08064 100644
--- a/java/res/xml/keyboard_layout_set_east_slavic.xml
+++ b/java/res/xml/keyboard_layout_set_east_slavic.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="cyrillic" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_east_slavic"
diff --git a/java/res/xml/keyboard_layout_set_farsi.xml b/java/res/xml/keyboard_layout_set_farsi.xml
index b9a91e3..9b44b7b 100644
--- a/java/res/xml/keyboard_layout_set_farsi.xml
+++ b/java/res/xml/keyboard_layout_set_farsi.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="arabic" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_farsi"
diff --git a/java/res/xml/keyboard_layout_set_georgian.xml b/java/res/xml/keyboard_layout_set_georgian.xml
index 36d0916..a0a0608 100644
--- a/java/res/xml/keyboard_layout_set_georgian.xml
+++ b/java/res/xml/keyboard_layout_set_georgian.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="georgian" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_georgian"
diff --git a/java/res/xml/keyboard_layout_set_greek.xml b/java/res/xml/keyboard_layout_set_greek.xml
index b376e4f..a1e738f 100644
--- a/java/res/xml/keyboard_layout_set_greek.xml
+++ b/java/res/xml/keyboard_layout_set_greek.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="greek" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_greek"
diff --git a/java/res/xml/keyboard_layout_set_hebrew.xml b/java/res/xml/keyboard_layout_set_hebrew.xml
index d5b25b3..d3d4b76 100644
--- a/java/res/xml/keyboard_layout_set_hebrew.xml
+++ b/java/res/xml/keyboard_layout_set_hebrew.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="hebrew" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_hebrew"
diff --git a/java/res/xml/keyboard_layout_set_mongolian.xml b/java/res/xml/keyboard_layout_set_mongolian.xml
index 2d364f6..977fc68 100644
--- a/java/res/xml/keyboard_layout_set_mongolian.xml
+++ b/java/res/xml/keyboard_layout_set_mongolian.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="cyrillic" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_mongolian"
diff --git a/java/res/xml/keyboard_layout_set_south_slavic.xml b/java/res/xml/keyboard_layout_set_south_slavic.xml
index 36666b9..b851a99 100644
--- a/java/res/xml/keyboard_layout_set_south_slavic.xml
+++ b/java/res/xml/keyboard_layout_set_south_slavic.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="cyrillic" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_south_slavic"
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 865ff07..1a76f3b 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -112,6 +112,8 @@
 
     private static final int PENDING_IMS_CALLBACK_DURATION = 800;
 
+    private static final int DELAY_WAIT_FOR_DICTIONARY_LOAD = 2000; // 2s
+
     private static final int PERIOD_FOR_AUDIO_AND_HAPTIC_FEEDBACK_IN_KEY_REPEAT = 2;
 
     /**
@@ -171,8 +173,9 @@
         private static final int MSG_REOPEN_DICTIONARIES = 5;
         private static final int MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED = 6;
         private static final int MSG_RESET_CACHES = 7;
+        private static final int MSG_WAIT_FOR_DICTIONARY_LOAD = 8;
         // Update this when adding new messages
-        private static final int MSG_LAST = MSG_RESET_CACHES;
+        private static final int MSG_LAST = MSG_WAIT_FOR_DICTIONARY_LOAD;
 
         private static final int ARG1_NOT_GESTURE_INPUT = 0;
         private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
@@ -234,7 +237,7 @@
                 latinIme.resetSuggest();
                 // We need to re-evaluate the currently composing word in case the script has
                 // changed.
-                postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */);
+                postWaitForDictionaryLoad();
                 break;
             case MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED:
                 latinIme.mInputLogic.onUpdateTailBatchInputCompleted(
@@ -253,6 +256,9 @@
                             latinIme.getCurrentRecapitalizeState());
                 }
                 break;
+            case MSG_WAIT_FOR_DICTIONARY_LOAD:
+                Log.i(TAG, "Timeout waiting for dictionary load");
+                break;
             }
         }
 
@@ -264,7 +270,8 @@
             sendMessage(obtainMessage(MSG_REOPEN_DICTIONARIES));
         }
 
-        public void postResumeSuggestions(final boolean shouldIncludeResumedWordInSuggestions) {
+        public void postResumeSuggestions(final boolean shouldIncludeResumedWordInSuggestions,
+                final boolean shouldDelay) {
             final LatinIME latinIme = getOwnerInstance();
             if (latinIme == null) {
                 return;
@@ -274,10 +281,16 @@
                 return;
             }
             removeMessages(MSG_RESUME_SUGGESTIONS);
-            sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS,
-                    shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE,
-                            0 /* ignored */),
-                    mDelayUpdateSuggestions);
+            if (shouldDelay) {
+                sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS,
+                                shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE,
+                                0 /* ignored */),
+                        mDelayUpdateSuggestions);
+            } else {
+                sendMessage(obtainMessage(MSG_RESUME_SUGGESTIONS,
+                        shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE,
+                        0 /* ignored */));
+            }
         }
 
         public void postResetCaches(final boolean tryResumeSuggestions, final int remainingTries) {
@@ -286,6 +299,19 @@
                     remainingTries, null));
         }
 
+        public void postWaitForDictionaryLoad() {
+            sendMessageDelayed(obtainMessage(MSG_WAIT_FOR_DICTIONARY_LOAD),
+                    DELAY_WAIT_FOR_DICTIONARY_LOAD);
+        }
+
+        public void cancelWaitForDictionaryLoad() {
+            removeMessages(MSG_WAIT_FOR_DICTIONARY_LOAD);
+        }
+
+        public boolean hasPendingWaitForDictionaryLoad() {
+            return hasMessages(MSG_WAIT_FOR_DICTIONARY_LOAD);
+        }
+
         public void cancelUpdateSuggestionStrip() {
             removeMessages(MSG_UPDATE_SUGGESTION_STRIP);
         }
@@ -582,6 +608,11 @@
         if (mainKeyboardView != null) {
             mainKeyboardView.setMainDictionaryAvailability(isMainDictionaryAvailable);
         }
+        if (mHandler.hasPendingWaitForDictionaryLoad()) {
+            mHandler.cancelWaitForDictionaryLoad();
+            mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
+                    false /* shouldDelay */);
+        }
     }
 
     private void resetSuggest() {
@@ -821,7 +852,8 @@
             // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best
             // effort to work around this bug.
             mInputLogic.mConnection.tryFixLyingCursorPosition();
-            mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */);
+            mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
+                    true /* shouldDelay */);
             canReachInputConnection = true;
         }
 
@@ -1336,6 +1368,17 @@
     }
 
     private void setSuggestedWords(final SuggestedWords suggestedWords) {
+        if (SuggestedWords.EMPTY != suggestedWords) {
+            final String autoCorrection;
+            if (suggestedWords.mWillAutoCorrect) {
+                autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION);
+            } else {
+                // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD)
+                // because it may differ from mWordComposer.mTypedWord.
+                autoCorrection = suggestedWords.mTypedWord;
+            }
+            mInputLogic.mWordComposer.setAutoCorrection(autoCorrection);
+        }
         mInputLogic.setSuggestedWords(suggestedWords);
         // TODO: Modify this when we support suggestions with hard keyboard
         if (!hasSuggestionStripView()) {
@@ -1399,18 +1442,9 @@
     public void showSuggestionStrip(final SuggestedWords sourceSuggestedWords) {
         final SuggestedWords suggestedWords =
                 sourceSuggestedWords.isEmpty() ? SuggestedWords.EMPTY : sourceSuggestedWords;
-        final String autoCorrection;
-        if (suggestedWords.mWillAutoCorrect) {
-            autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION);
-        } else {
-            // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD)
-            // because it may differ from mWordComposer.mTypedWord.
-            autoCorrection = sourceSuggestedWords.mTypedWord;
-        }
         if (SuggestedWords.EMPTY == suggestedWords) {
             setNeutralSuggestionStrip();
         } else {
-            mInputLogic.mWordComposer.setAutoCorrection(autoCorrection);
             setSuggestedWords(suggestedWords);
         }
         // Cache the auto-correction in accessibility code so we can speak it if the user
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 5e0dafa..fdd47a4 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -30,6 +30,7 @@
 import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
 import com.android.inputmethod.latin.utils.CapsModeUtils;
 import com.android.inputmethod.latin.utils.DebugLogUtils;
+import com.android.inputmethod.latin.utils.ScriptUtils;
 import com.android.inputmethod.latin.utils.SpannableStringUtils;
 import com.android.inputmethod.latin.utils.StringUtils;
 import com.android.inputmethod.latin.utils.TextRange;
@@ -623,9 +624,10 @@
      * Returns the text surrounding the cursor.
      *
      * @param sortedSeparators a sorted array of code points that split words.
+     * @param scriptId the script we consider to be writing words, as one of ScriptUtils.SCRIPT_*
      * @return a range containing the text surrounding the cursor
      */
-    public TextRange getWordRangeAtCursor(final int[] sortedSeparators) {
+    public TextRange getWordRangeAtCursor(final int[] sortedSeparators, final int scriptId) {
         mIC = mParent.getCurrentInputConnection();
         if (mIC == null) {
             return null;
@@ -642,7 +644,8 @@
         int startIndexInBefore = before.length();
         while (startIndexInBefore > 0) {
             final int codePoint = Character.codePointBefore(before, startIndexInBefore);
-            if (isSeparator(codePoint, sortedSeparators)) {
+            if (isSeparator(codePoint, sortedSeparators)
+                    || !ScriptUtils.isLetterPartOfScript(codePoint, scriptId)) {
                 break;
             }
             --startIndexInBefore;
@@ -655,7 +658,8 @@
         int endIndexInAfter = -1;
         while (++endIndexInAfter < after.length()) {
             final int codePoint = Character.codePointAt(after, endIndexInAfter);
-            if (isSeparator(codePoint, sortedSeparators)) {
+            if (isSeparator(codePoint, sortedSeparators)
+                    || !ScriptUtils.isLetterPartOfScript(codePoint, scriptId)) {
                 break;
             }
             if (Character.isSupplementaryCodePoint(codePoint)) {
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index b9a87c9..7a4b726 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -362,7 +362,8 @@
         // The cursor has been moved : we now accept to perform recapitalization
         mRecapitalizeStatus.enable();
         // We moved the cursor. If we are touching a word, we need to resume suggestion.
-        mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */);
+        mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */,
+                true /* shouldDelay */);
         // Stop the last recapitalization, if started.
         mRecapitalizeStatus.stop();
         return true;
@@ -1288,9 +1289,14 @@
             return;
         }
         final TextRange range = mConnection.getWordRangeAtCursor(
-                settingsValues.mSpacingAndPunctuations.mSortedWordSeparators);
+                settingsValues.mSpacingAndPunctuations.mSortedWordSeparators,
+                currentKeyboardScriptId);
         if (null == range) return; // Happens if we don't have an input connection at all
-        if (range.length() <= 0) return; // Race condition. No text to resume on, so bail out.
+        if (range.length() <= 0) {
+            // Race condition, or touching a word in a non-supported script.
+            mLatinIME.setNeutralSuggestionStrip();
+            return;
+        }
         // If for some strange reason (editor bug or so) we measure the text before the cursor as
         // longer than what the entire text is supposed to be, the safe thing to do is bail out.
         if (range.mHasUrlSpans) return; // If there are links, we don't resume suggestions. Making
@@ -1945,7 +1951,8 @@
         if (tryResumeSuggestions) {
             // This is triggered when starting input anew, so we want to include the resumed
             // word in suggestions.
-            handler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */);
+            handler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
+                    true /* shouldDelay */);
         }
         return true;
     }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index d031624..b57eab3 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -48,7 +48,6 @@
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
-import java.util.TreeMap;
 
 /**
  * Service for spell checking, using LatinIME's dictionaries and mechanisms.
@@ -373,7 +372,7 @@
     }
 
     public DictAndKeyboard createDictAndKeyboard(final Locale locale) {
-        final int script = ScriptUtils.getScriptFromLocale(locale);
+        final int script = ScriptUtils.getScriptFromSpellCheckerLocale(locale);
         final String keyboardLayoutName = getKeyboardLayoutNameForScript(script);
         final InputMethodSubtype subtype = AdditionalSubtypeUtils.createAdditionalSubtype(
                 locale.toString(), keyboardLayoutName, null);
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index be33f33..4825b9e 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -117,7 +117,7 @@
         final String localeString = getLocale();
         mDictionaryPool = mService.getDictionaryPool(localeString);
         mLocale = LocaleUtils.constructLocaleFromString(localeString);
-        mScript = ScriptUtils.getScriptFromLocale(mLocale);
+        mScript = ScriptUtils.getScriptFromSpellCheckerLocale(mLocale);
     }
 
     @Override
@@ -152,7 +152,7 @@
         // Filter by first letter
         final int firstCodePoint = text.codePointAt(0);
         // Filter out words that don't start with a letter or an apostrophe
-        if (!ScriptUtils.isLetterCheckableByScript(firstCodePoint, script)
+        if (!ScriptUtils.isLetterPartOfScript(firstCodePoint, script)
                 && '\'' != firstCodePoint) return CHECKABILITY_FIRST_LETTER_UNCHECKABLE;
 
         // Filter contents
@@ -173,7 +173,7 @@
             if (Constants.CODE_PERIOD == codePoint) {
                 return CHECKABILITY_CONTAINS_PERIOD;
             }
-            if (ScriptUtils.isLetterCheckableByScript(codePoint, script)) ++letterCount;
+            if (ScriptUtils.isLetterPartOfScript(codePoint, script)) ++letterCount;
         }
         // Guestimate heuristic: perform spell checking if at least 3/4 of the characters
         // in this word are letters
diff --git a/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java b/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java
index 9ab7c77..a76a6df 100644
--- a/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java
@@ -31,7 +31,9 @@
     public static final int SCRIPT_GREEK = 2;
     public static final int SCRIPT_ARABIC = 3;
     public static final int SCRIPT_HEBREW = 4;
-    public static final TreeMap<String, Integer> mLanguageToScript;
+    public static final int SCRIPT_ARMENIAN = 5;
+    public static final int SCRIPT_GEORGIAN = 6;
+    public static final TreeMap<String, Integer> mSpellCheckerLanguageToScript;
     static {
         // List of the supported languages and their associated script. We won't check
         // words written in another script than the selected script, because we know we
@@ -41,24 +43,24 @@
         // proximity to pass to the dictionary descent algorithm.
         // IMPORTANT: this only contains languages - do not write countries in there.
         // Only the language is searched from the map.
-        mLanguageToScript = new TreeMap<>();
-        mLanguageToScript.put("cs", SCRIPT_LATIN);
-        mLanguageToScript.put("da", SCRIPT_LATIN);
-        mLanguageToScript.put("de", SCRIPT_LATIN);
-        mLanguageToScript.put("el", SCRIPT_GREEK);
-        mLanguageToScript.put("en", SCRIPT_LATIN);
-        mLanguageToScript.put("es", SCRIPT_LATIN);
-        mLanguageToScript.put("fi", SCRIPT_LATIN);
-        mLanguageToScript.put("fr", SCRIPT_LATIN);
-        mLanguageToScript.put("hr", SCRIPT_LATIN);
-        mLanguageToScript.put("it", SCRIPT_LATIN);
-        mLanguageToScript.put("lt", SCRIPT_LATIN);
-        mLanguageToScript.put("lv", SCRIPT_LATIN);
-        mLanguageToScript.put("nb", SCRIPT_LATIN);
-        mLanguageToScript.put("nl", SCRIPT_LATIN);
-        mLanguageToScript.put("pt", SCRIPT_LATIN);
-        mLanguageToScript.put("sl", SCRIPT_LATIN);
-        mLanguageToScript.put("ru", SCRIPT_CYRILLIC);
+        mSpellCheckerLanguageToScript = new TreeMap<>();
+        mSpellCheckerLanguageToScript.put("cs", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("da", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("de", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("el", SCRIPT_GREEK);
+        mSpellCheckerLanguageToScript.put("en", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("es", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("fi", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("fr", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("hr", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("it", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("lt", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("lv", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("nb", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("nl", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("pt", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("sl", SCRIPT_LATIN);
+        mSpellCheckerLanguageToScript.put("ru", SCRIPT_CYRILLIC);
     }
     /*
      * Returns whether the code point is a letter that makes sense for the specified
@@ -68,8 +70,8 @@
      * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters
      * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters.
      */
-    public static boolean isLetterCheckableByScript(final int codePoint, final int script) {
-        switch (script) {
+    public static boolean isLetterPartOfScript(final int codePoint, final int scriptId) {
+        switch (scriptId) {
         case SCRIPT_LATIN:
             // Our supported latin script dictionaries (EFIGS) at the moment only include
             // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode
@@ -91,16 +93,45 @@
             return (codePoint >= 0x370 && codePoint <= 0x3FF)
                     || (codePoint >= 0x1F00 && codePoint <= 0x1FFF)
                     || codePoint == 0xF2;
+        case SCRIPT_ARABIC:
+            // Arabic letters can be in any of the following blocks:
+            // Arabic U+0600..U+06FF
+            // Arabic Supplement U+0750..U+077F
+            // Arabic Extended-A U+08A0..U+08FF
+            // Arabic Presentation Forms-A U+FB50..U+FDFF
+            // Arabic Presentation Forms-B U+FE70..U+FEFF
+            return (codePoint >= 0x600 && codePoint <= 0x6FF)
+                    || (codePoint >= 0x750 && codePoint <= 0x77F)
+                    || (codePoint >= 0x8A0 && codePoint <= 0x8FF)
+                    || (codePoint >= 0xFB50 && codePoint <= 0xFDFF)
+                    || (codePoint >= 0xFE70 && codePoint <= 0xFEFF);
+        case SCRIPT_HEBREW:
+            // Hebrew letters are in the Hebrew unicode block, which spans from U+0590 to U+05FF,
+            // or in the Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the
+            // Hebrew part of that block, which is U+FB1D..U+FB4F.
+            return (codePoint >= 0x590 && codePoint <= 0x5FF
+                    || codePoint >= 0xFB1D && codePoint <= 0xFB4F);
+        case SCRIPT_ARMENIAN:
+            // Armenian letters are in the Armenian unicode block, U+0530..U+058F and
+            // Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the Armenian part
+            // of that block, which is U+FB13..U+FB17.
+            return (codePoint >= 0x530 && codePoint <= 0x58F
+                    || codePoint >= 0xFB13 && codePoint <= 0xFB17);
+        case SCRIPT_GEORGIAN:
+            // Georgian letters are in the Georgian unicode block, U+10A0..U+10FF,
+            // or Georgian supplement block, U+2D00..U+2D2F
+            return (codePoint >= 0x10A0 && codePoint <= 0x10FF
+                    || codePoint >= 0x2D00 && codePoint <= 0x2D2F);
         case SCRIPT_UNKNOWN:
             return true;
         default:
             // Should never come here
-            throw new RuntimeException("Impossible value of script: " + script);
+            throw new RuntimeException("Impossible value of script: " + scriptId);
         }
     }
 
-    public static int getScriptFromLocale(final Locale locale) {
-        final Integer script = mLanguageToScript.get(locale.getLanguage());
+    public static int getScriptFromSpellCheckerLocale(final Locale locale) {
+        final Integer script = mSpellCheckerLanguageToScript.get(locale.getLanguage());
         if (null == script) {
             throw new RuntimeException("We have been called with an unsupported language: \""
                     + locale.getLanguage() + "\". Framework bug?");
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index efb076f..de9475a 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -626,13 +626,17 @@
 
     public void testSwitchLanguages() {
         final String WORD_TO_TYPE_FIRST_PART = "com";
-        final String WORD_TO_TYPE_SECOND_PART = "md ";
-        final String EXPECTED_RESULT = "comme ";
+        final String WORD_TO_TYPE_SECOND_PART = "md";
+        final String EXPECTED_RESULT = "comme";
         changeLanguage("en");
         type(WORD_TO_TYPE_FIRST_PART);
         changeLanguage("fr");
+        runMessages();
         type(WORD_TO_TYPE_SECOND_PART);
-        assertEquals("Composing continues after switching languages", EXPECTED_RESULT,
-                mEditText.getText().toString());
+        sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
+        runMessages();
+        final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
+        assertEquals("Suggestions updated after switching languages",
+                    EXPECTED_RESULT, suggestedWords.size() > 0 ? suggestedWords.getWord(1) : null);
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
index 04a55af..2d92e69 100644
--- a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
+++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
@@ -33,6 +33,7 @@
 import com.android.inputmethod.latin.PrevWordsInfo.WordInfo;
 import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
 import com.android.inputmethod.latin.utils.RunInLocale;
+import com.android.inputmethod.latin.utils.ScriptUtils;
 import com.android.inputmethod.latin.utils.StringUtils;
 import com.android.inputmethod.latin.utils.TextRange;
 
@@ -221,6 +222,8 @@
     private static final int[] SPACE_TAB = StringUtils.toSortedCodePointArray(" \t");
     // A character that needs surrogate pair to represent its code point (U+2008A).
     private static final String SUPPLEMENTARY_CHAR = "\uD840\uDC8A";
+    private static final String HIRAGANA_WORD = "\u3042\u3044\u3046\u3048\u304A"; // あいうえお
+    private static final String GREEK_WORD = "\u03BA\u03B1\u03B9"; // και
 
     public void testGetWordRangeAtCursor() {
         ExtractedText et = new ExtractedText();
@@ -233,13 +236,13 @@
 
         ic.beginBatchEdit();
         // basic case
-        r = ic.getWordRangeAtCursor(SPACE);
+        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
         assertTrue(TextUtils.equals("word", r.mWord));
 
         // tab character instead of space
         mockInputMethodService.setInputConnection(new MockConnection("one\tword\two", "rd", et));
         ic.beginBatchEdit();
-        r = ic.getWordRangeAtCursor(TAB);
+        r = ic.getWordRangeAtCursor(TAB, ScriptUtils.SCRIPT_LATIN);
         ic.endBatchEdit();
         assertTrue(TextUtils.equals("word", r.mWord));
 
@@ -247,9 +250,28 @@
         mockInputMethodService.setInputConnection(
                 new MockConnection("one word" + SUPPLEMENTARY_CHAR + "wo", "rd", et));
         ic.beginBatchEdit();
-        r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR));
+        r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR),
+                ScriptUtils.SCRIPT_LATIN);
         ic.endBatchEdit();
         assertTrue(TextUtils.equals("word", r.mWord));
+
+        // split on chars outside the specified script
+        mockInputMethodService.setInputConnection(
+                new MockConnection(HIRAGANA_WORD + "wo", "rd" + GREEK_WORD, et));
+        ic.beginBatchEdit();
+        r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR),
+                ScriptUtils.SCRIPT_LATIN);
+        ic.endBatchEdit();
+        assertTrue(TextUtils.equals("word", r.mWord));
+
+        // likewise for greek
+        mockInputMethodService.setInputConnection(
+                new MockConnection("text" + GREEK_WORD, "text", et));
+        ic.beginBatchEdit();
+        r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR),
+                ScriptUtils.SCRIPT_GREEK);
+        ic.endBatchEdit();
+        assertTrue(TextUtils.equals(GREEK_WORD, r.mWord));
     }
 
     /**
@@ -277,7 +299,7 @@
         TextRange r;
         SuggestionSpan[] suggestions;
 
-        r = ic.getWordRangeAtCursor(SPACE);
+        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
         suggestions = r.getSuggestionSpansAtWord();
         assertEquals(suggestions.length, 1);
         MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
@@ -289,7 +311,7 @@
         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
                 10 /* start */, 16 /* end */, 0 /* flags */);
         mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
-        r = ic.getWordRangeAtCursor(SPACE);
+        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
         suggestions = r.getSuggestionSpansAtWord();
         assertEquals(suggestions.length, 2);
         MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
@@ -302,7 +324,7 @@
         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
                 5 /* start */, 16 /* end */, 0 /* flags */);
         mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
-        r = ic.getWordRangeAtCursor(SPACE);
+        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
         suggestions = r.getSuggestionSpansAtWord();
         assertEquals(suggestions.length, 1);
         MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
@@ -314,7 +336,7 @@
         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
                 10 /* start */, 20 /* end */, 0 /* flags */);
         mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
-        r = ic.getWordRangeAtCursor(SPACE);
+        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
         suggestions = r.getSuggestionSpansAtWord();
         assertEquals(suggestions.length, 1);
         MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
@@ -326,7 +348,7 @@
         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
                 5 /* start */, 20 /* end */, 0 /* flags */);
         mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
-        r = ic.getWordRangeAtCursor(SPACE);
+        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
         suggestions = r.getSuggestionSpansAtWord();
         assertEquals(suggestions.length, 1);
         MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
@@ -338,7 +360,7 @@
         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
                 5 /* start */, 20 /* end */, 0 /* flags */);
         mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
-        r = ic.getWordRangeAtCursor(SPACE);
+        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
         suggestions = r.getSuggestionSpansAtWord();
         assertEquals(suggestions.length, 0);
     }