Merge "Fix bug and Add large test for decaying dictionary."
diff --git a/dictionaries/en_GB_wordlist.combined.gz b/dictionaries/en_GB_wordlist.combined.gz
index 839f3ef..4f008ed 100644
--- a/dictionaries/en_GB_wordlist.combined.gz
+++ b/dictionaries/en_GB_wordlist.combined.gz
Binary files differ
diff --git a/java/res/layout/emoji_palettes_view.xml b/java/res/layout/emoji_palettes_view.xml
index 6133326..1c6da90 100644
--- a/java/res/layout/emoji_palettes_view.xml
+++ b/java/res/layout/emoji_palettes_view.xml
@@ -101,10 +101,10 @@
             android:layout_weight="0.70"
             android:layout_height="match_parent" />
         <ImageButton
-            android:id="@+id/emoji_keyboard_send"
+            android:id="@+id/emoji_keyboard_alphabet2"
             android:layout_width="0dip"
             android:layout_weight="0.15"
             android:layout_height="match_parent"
-            android:src="@drawable/sym_keyboard_return_holo_dark" />
+            android:src="@drawable/ic_ime_switcher_dark" />
     </LinearLayout>
 </com.android.inputmethod.keyboard.EmojiPalettesView>
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index 46e72bf..85ae500 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -484,10 +484,10 @@
         spaceKey.setTag(Constants.CODE_SPACE);
         spaceKey.setOnClickListener(this);
         emojiLp.setKeyProperties(spaceKey);
-        final ImageView sendKey = (ImageView)findViewById(R.id.emoji_keyboard_send);
-        sendKey.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
-        sendKey.setTag(Constants.CODE_ENTER);
-        sendKey.setOnClickListener(this);
+        final ImageView alphabetKey2 = (ImageView)findViewById(R.id.emoji_keyboard_alphabet2);
+        alphabetKey2.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
+        alphabetKey2.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL);
+        alphabetKey2.setOnClickListener(this);
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index eb6d7c1..503b18b 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -204,10 +204,20 @@
         return AndroidSpellCheckerSessionFactory.newInstance(this);
     }
 
-    public static SuggestionsInfo getNotInDictEmptySuggestions() {
-        return new SuggestionsInfo(0, EMPTY_STRING_ARRAY);
+    /**
+     * Returns an empty SuggestionsInfo with flags signaling the word is not in the dictionary.
+     * @param reportAsTypo whether this should include the flag LOOKS_LIKE_TYPO, for red underline.
+     * @return the empty SuggestionsInfo with the appropriate flags set.
+     */
+    public static SuggestionsInfo getNotInDictEmptySuggestions(final boolean reportAsTypo) {
+        return new SuggestionsInfo(reportAsTypo ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0,
+                EMPTY_STRING_ARRAY);
     }
 
+    /**
+     * Returns an empty suggestionInfo with flags signaling the word is in the dictionary.
+     * @return the empty SuggestionsInfo with the appropriate flags set.
+     */
     public static SuggestionsInfo getInDictEmptySuggestions() {
         return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY,
                 EMPTY_STRING_ARRAY);
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 69f9a46..d6e5b75 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -161,6 +161,12 @@
         }
     }
 
+    private static final int CHECKABILITY_CHECKABLE = 0;
+    private static final int CHECKABILITY_TOO_MANY_NON_LETTERS = 1;
+    private static final int CHECKABILITY_CONTAINS_PERIOD = 2;
+    private static final int CHECKABILITY_EMAIL_OR_URL = 3;
+    private static final int CHECKABILITY_FIRST_LETTER_UNCHECKABLE = 4;
+    private static final int CHECKABILITY_TOO_SHORT = 5;
     /**
      * Finds out whether a particular string should be filtered out of spell checking.
      *
@@ -171,10 +177,10 @@
      *
      * @param text the string to evaluate.
      * @param script the identifier for the script this spell checker recognizes
-     * @return true if we should filter this text out, false otherwise
+     * @return one of the FILTER_OUT_* constants above.
      */
-    private static boolean shouldFilterOut(final String text, final int script) {
-        if (TextUtils.isEmpty(text) || text.length() <= 1) return true;
+    private static int getCheckabilityInScript(final String text, final int script) {
+        if (TextUtils.isEmpty(text) || text.length() <= 1) return CHECKABILITY_TOO_SHORT;
 
         // TODO: check if an equivalent processing can't be done more quickly with a
         // compiled regexp.
@@ -182,7 +188,7 @@
         final int firstCodePoint = text.codePointAt(0);
         // Filter out words that don't start with a letter or an apostrophe
         if (!isLetterCheckableByLanguage(firstCodePoint, script)
-                && '\'' != firstCodePoint) return true;
+                && '\'' != firstCodePoint) return CHECKABILITY_FIRST_LETTER_UNCHECKABLE;
 
         // Filter contents
         final int length = text.length();
@@ -193,13 +199,21 @@
             // Any word containing a SLASH is probably either an ad-hoc combination of two
             // words or a URI - in either case we don't want to spell check that
             if (Constants.CODE_COMMERCIAL_AT == codePoint || Constants.CODE_SLASH == codePoint) {
-                return true;
+                return CHECKABILITY_EMAIL_OR_URL;
+            }
+            // If the string contains a period, native returns strange suggestions (it seems
+            // to return suggestions for everything up to the period only and to ignore the
+            // rest), so we suppress lookup if there is a period.
+            // TODO: investigate why native returns these suggestions and remove this code.
+            if (Constants.CODE_PERIOD == codePoint) {
+                return CHECKABILITY_CONTAINS_PERIOD;
             }
             if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount;
         }
         // Guestimate heuristic: perform spell checking if at least 3/4 of the characters
         // in this word are letters
-        return (letterCount * 4 < length * 3);
+        return (letterCount * 4 < length * 3)
+                ? CHECKABILITY_TOO_MANY_NON_LETTERS : CHECKABILITY_CHECKABLE;
     }
 
     /**
@@ -256,16 +270,20 @@
                         cachedSuggestionsParams.mFlags, cachedSuggestionsParams.mSuggestions);
             }
 
-            if (shouldFilterOut(inText, mScript)) {
+            final int checkability = getCheckabilityInScript(inText, mScript);
+            if (CHECKABILITY_CHECKABLE != checkability) {
                 DictAndKeyboard dictInfo = null;
                 try {
                     dictInfo = mDictionaryPool.pollWithDefaultTimeout();
                     if (!DictionaryPool.isAValidDictionary(dictInfo)) {
-                        return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+                        return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
+                                false /* reportAsTypo */);
                     }
                     return dictInfo.mDictionary.isValidWord(inText)
                             ? AndroidSpellCheckerService.getInDictEmptySuggestions()
-                            : AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+                            : AndroidSpellCheckerService.getNotInDictEmptySuggestions(
+                                    CHECKABILITY_CONTAINS_PERIOD == checkability
+                                    /* reportAsTypo */);
                 } finally {
                     if (null != dictInfo) {
                         if (!mDictionaryPool.offer(dictInfo)) {
@@ -290,7 +308,8 @@
             try {
                 dictInfo = mDictionaryPool.pollWithDefaultTimeout();
                 if (!DictionaryPool.isAValidDictionary(dictInfo)) {
-                    return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+                    return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
+                            false /* reportAsTypo */);
                 }
                 final WordComposer composer = new WordComposer();
                 final int length = text.length();
@@ -351,7 +370,8 @@
                 throw e;
             } else {
                 Log.e(TAG, "Exception while spellcheking", e);
-                return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+                return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
+                        false /* reportAsTypo */);
             }
         }
     }