Merge "Let accessibility users know to connect a headset when the IME connects to a password field."
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 1e2ea55..f1ae0b3 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -18,7 +18,6 @@
 
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
 import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
@@ -34,6 +33,8 @@
 import com.android.inputmethod.keyboard.internal.MoreKeySpecParser;
 import com.android.inputmethod.latin.R;
 
+import org.xmlpull.v1.XmlPullParser;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -206,7 +207,7 @@
      * @param keyStyles active key styles set
      */
     public Key(Resources res, KeyboardParams params, KeyboardBuilder.Row row,
-            XmlResourceParser parser, KeyStyles keyStyles) {
+            XmlPullParser parser, KeyStyles keyStyles) {
         final float horizontalGap = isSpacer() ? 0 : params.mHorizontalGap;
         final int keyHeight = row.mRowHeight;
         mVerticalGap = params.mVerticalGap;
@@ -536,7 +537,7 @@
 
     public static class Spacer extends Key {
         public Spacer(Resources res, KeyboardParams params, KeyboardBuilder.Row row,
-                XmlResourceParser parser, KeyStyles keyStyles) {
+                XmlPullParser parser, KeyStyles keyStyles) {
             super(res, params, row, parser, keyStyles);
         }
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 49e92fd..83871a6 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -649,7 +649,7 @@
     }
 
     public boolean isVibrateAndSoundFeedbackRequired() {
-        return mKeyboardView == null || !mKeyboardView.isInSlidingKeyInput();
+        return mKeyboardView != null && !mKeyboardView.isInSlidingKeyInput();
     }
 
     private int getPointerCount() {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
index 3d2d77c..b385b7a 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
@@ -17,12 +17,13 @@
 package com.android.inputmethod.keyboard.internal;
 
 import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
 import android.util.Log;
 
 import com.android.inputmethod.keyboard.internal.KeyboardBuilder.ParseException;
 import com.android.inputmethod.latin.R;
 
+import org.xmlpull.v1.XmlPullParser;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -210,7 +211,7 @@
     }
 
     public void parseKeyStyleAttributes(TypedArray keyStyleAttr, TypedArray keyAttrs,
-            XmlResourceParser parser) {
+            XmlPullParser parser) {
         final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
         if (DEBUG) Log.d(TAG, String.format("<%s styleName=%s />",
                 KeyboardBuilder.TAG_KEY_STYLE, styleName));
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index e73f5a0..dd31d17 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
@@ -160,7 +159,7 @@
         // Will be updated by {@link Key}'s constructor.
         private float mCurrentX;
 
-        public Row(Resources res, KeyboardParams params, XmlResourceParser parser, int y) {
+        public Row(Resources res, KeyboardParams params, XmlPullParser parser, int y) {
             mParams = params;
             TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
                     R.styleable.Keyboard);
@@ -291,7 +290,7 @@
 
     private void parseKeyboard(int resId) throws XmlPullParserException, IOException {
         if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId));
-        final XmlResourceParser parser = mResources.getXml(resId);
+        final XmlPullParser parser = mResources.getXml(resId);
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
             if (event == XmlPullParser.START_TAG) {
@@ -311,7 +310,7 @@
     public static String parseKeyboardLocale(
             Context context, int resId) throws XmlPullParserException, IOException {
         final Resources res = context.getResources();
-        final XmlResourceParser parser = res.getXml(resId);
+        final XmlPullParser parser = res.getXml(resId);
         if (parser == null) return "";
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -329,7 +328,7 @@
         return "";
     }
 
-    private void parseKeyboardAttributes(XmlResourceParser parser) {
+    private void parseKeyboardAttributes(XmlPullParser parser) {
         final int displayWidth = mDisplayMetrics.widthPixels;
         final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
                 Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle,
@@ -392,7 +391,7 @@
         }
     }
 
-    private void parseKeyboardContent(XmlResourceParser parser, boolean skip)
+    private void parseKeyboardContent(XmlPullParser parser, boolean skip)
             throws XmlPullParserException, IOException {
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -431,7 +430,7 @@
         }
     }
 
-    private Row parseRowAttributes(XmlResourceParser parser) {
+    private Row parseRowAttributes(XmlPullParser parser) {
         final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard);
         try {
@@ -445,7 +444,7 @@
         }
     }
 
-    private void parseRowContent(XmlResourceParser parser, Row row, boolean skip)
+    private void parseRowContent(XmlPullParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -484,7 +483,7 @@
         }
     }
 
-    private void parseKey(XmlResourceParser parser, Row row, boolean skip)
+    private void parseKey(XmlPullParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         if (skip) {
             checkEndTag(TAG_KEY, parser);
@@ -498,7 +497,7 @@
         }
     }
 
-    private void parseSpacer(XmlResourceParser parser, Row row, boolean skip)
+    private void parseSpacer(XmlPullParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         if (skip) {
             checkEndTag(TAG_SPACER, parser);
@@ -510,17 +509,17 @@
         }
     }
 
-    private void parseIncludeKeyboardContent(XmlResourceParser parser, boolean skip)
+    private void parseIncludeKeyboardContent(XmlPullParser parser, boolean skip)
             throws XmlPullParserException, IOException {
         parseIncludeInternal(parser, null, skip);
     }
 
-    private void parseIncludeRowContent(XmlResourceParser parser, Row row, boolean skip)
+    private void parseIncludeRowContent(XmlPullParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         parseIncludeInternal(parser, row, skip);
     }
 
-    private void parseIncludeInternal(XmlResourceParser parser, Row row, boolean skip)
+    private void parseIncludeInternal(XmlPullParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         if (skip) {
             checkEndTag(TAG_INCLUDE, parser);
@@ -540,7 +539,7 @@
         }
     }
 
-    private void parseMerge(XmlResourceParser parser, Row row, boolean skip)
+    private void parseMerge(XmlPullParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -561,17 +560,17 @@
         }
     }
 
-    private void parseSwitchKeyboardContent(XmlResourceParser parser, boolean skip)
+    private void parseSwitchKeyboardContent(XmlPullParser parser, boolean skip)
             throws XmlPullParserException, IOException {
         parseSwitchInternal(parser, null, skip);
     }
 
-    private void parseSwitchRowContent(XmlResourceParser parser, Row row, boolean skip)
+    private void parseSwitchRowContent(XmlPullParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         parseSwitchInternal(parser, row, skip);
     }
 
-    private void parseSwitchInternal(XmlResourceParser parser, Row row, boolean skip)
+    private void parseSwitchInternal(XmlPullParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mParams.mId));
         boolean selected = false;
@@ -598,7 +597,7 @@
         }
     }
 
-    private boolean parseCase(XmlResourceParser parser, Row row, boolean skip)
+    private boolean parseCase(XmlPullParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         final boolean selected = parseCaseCondition(parser);
         if (row == null) {
@@ -611,7 +610,7 @@
         return selected;
     }
 
-    private boolean parseCaseCondition(XmlResourceParser parser) {
+    private boolean parseCaseCondition(XmlPullParser parser) {
         final KeyboardId id = mParams.mId;
         if (id == null)
             return true;
@@ -718,7 +717,7 @@
         return false;
     }
 
-    private boolean parseDefault(XmlResourceParser parser, Row row, boolean skip)
+    private boolean parseDefault(XmlPullParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT));
         if (row == null) {
@@ -729,7 +728,7 @@
         return true;
     }
 
-    private void parseKeyStyle(XmlResourceParser parser, boolean skip) {
+    private void parseKeyStyle(XmlPullParser parser, boolean skip) {
         TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard_KeyStyle);
         TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
@@ -746,7 +745,7 @@
         }
     }
 
-    private static void checkEndTag(String tag, XmlResourceParser parser)
+    private static void checkEndTag(String tag, XmlPullParser parser)
             throws XmlPullParserException, IOException {
         if (parser.next() == XmlPullParser.END_TAG && tag.equals(parser.getName()))
             return;
@@ -839,35 +838,35 @@
 
     @SuppressWarnings("serial")
     public static class ParseException extends InflateException {
-        public ParseException(String msg, XmlResourceParser parser) {
+        public ParseException(String msg, XmlPullParser parser) {
             super(msg + " at line " + parser.getLineNumber());
         }
     }
 
     @SuppressWarnings("serial")
     private static class IllegalStartTag extends ParseException {
-        public IllegalStartTag(XmlResourceParser parser, String parent) {
+        public IllegalStartTag(XmlPullParser parser, String parent) {
             super("Illegal start tag " + parser.getName() + " in " + parent, parser);
         }
     }
 
     @SuppressWarnings("serial")
     private static class IllegalEndTag extends ParseException {
-        public IllegalEndTag(XmlResourceParser parser, String parent) {
+        public IllegalEndTag(XmlPullParser parser, String parent) {
             super("Illegal end tag " + parser.getName() + " in " + parent, parser);
         }
     }
 
     @SuppressWarnings("serial")
     private static class IllegalAttribute extends ParseException {
-        public IllegalAttribute(XmlResourceParser parser, String attribute) {
+        public IllegalAttribute(XmlPullParser parser, String attribute) {
             super("Tag " + parser.getName() + " has illegal attribute " + attribute, parser);
         }
     }
 
     @SuppressWarnings("serial")
     private static class NonEmptyTag extends ParseException {
-        public NonEmptyTag(String tag, XmlResourceParser parser) {
+        public NonEmptyTag(String tag, XmlPullParser parser) {
             super(tag + " must be empty tag", parser);
         }
     }
diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java
index fc97710..485ec51 100644
--- a/java/src/com/android/inputmethod/latin/AutoCorrection.java
+++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java
@@ -82,14 +82,16 @@
         return false;
     }
 
-    public static boolean isValidWordForAutoCorrection(
+    public static boolean allowsToBeAutoCorrected(
             Map<String, Dictionary> dictionaries, CharSequence word, boolean ignoreCase) {
-        final Dictionary whiteList = dictionaries.get(Suggest.DICT_KEY_WHITELIST);
+        final WhitelistDictionary whitelistDictionary =
+                (WhitelistDictionary)dictionaries.get(Suggest.DICT_KEY_WHITELIST);
         // If "word" is in the whitelist dictionary, it should not be auto corrected.
-        if (whiteList != null && whiteList.isValidWord(word)) {
-            return false;
+        if (whitelistDictionary != null
+                && whitelistDictionary.shouldForciblyAutoCorrectFrom(word)) {
+            return true;
         }
-        return isValidWord(dictionaries, word, ignoreCase);
+        return !isValidWord(dictionaries, word, ignoreCase);
     }
 
     private static boolean hasAutoCorrectionForWhitelistedWord(CharSequence whiteListedWord) {
@@ -100,8 +102,8 @@
             WordComposer wordComposer, ArrayList<CharSequence> suggestions, CharSequence typedWord,
             int correctionMode) {
         if (TextUtils.isEmpty(typedWord)) return false;
-        boolean isValidWord = isValidWordForAutoCorrection(dictionaries, typedWord, false);
-        return wordComposer.size() > 1 && suggestions.size() > 0 && isValidWord
+        boolean allowsAutoCorrect = allowsToBeAutoCorrected(dictionaries, typedWord, false);
+        return wordComposer.size() > 1 && suggestions.size() > 0 && !allowsAutoCorrect
                 && (correctionMode == Suggest.CORRECTION_FULL
                 || correctionMode == Suggest.CORRECTION_FULL_BIGRAM);
     }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 1278c5e..dfdb97b 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1265,6 +1265,7 @@
             break;
         case Keyboard.CODE_CAPSLOCK:
             switcher.toggleCapsLock();
+            vibrate();
             break;
         case Keyboard.CODE_SHORTCUT:
             mSubtypeSwitcher.switchToShortcutIME();
@@ -1640,11 +1641,16 @@
         boolean autoCorrectionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection();
         final CharSequence typedWord = wordComposer.getTypedWord();
         // Here, we want to promote a whitelisted word if exists.
-        final boolean typedWordValid = AutoCorrection.isValidWordForAutoCorrection(
+        // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
+        // but still autocorrected from - in the case the whitelist only capitalizes the word.
+        // The whitelist should be case-insensitive, so it's not possible to be consistent with
+        // a boolean flag. Right now this is handled with a slight hack in
+        // WhitelistDictionary#shouldForciblyAutoCorrectFrom.
+        final boolean allowsToBeAutoCorrected = AutoCorrection.allowsToBeAutoCorrected(
                 mSuggest.getUnigramDictionaries(), typedWord, preferCapitalization());
         if (mCorrectionMode == Suggest.CORRECTION_FULL
                 || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) {
-            autoCorrectionAvailable |= typedWordValid;
+            autoCorrectionAvailable |= (!allowsToBeAutoCorrected);
         }
         // Don't auto-correct words with multiple capital letter
         autoCorrectionAvailable &= !wordComposer.isMostlyCaps();
@@ -1657,9 +1663,9 @@
         // need to clear the previous state when the user starts typing a word (i.e. typed word's
         // length == 1).
         if (typedWord != null) {
-            if (builder.size() > 1 || typedWord.length() == 1 || typedWordValid
+            if (builder.size() > 1 || typedWord.length() == 1 || (!allowsToBeAutoCorrected)
                     || mSuggestionsView.isShowingAddToDictionaryHint()) {
-                builder.setTypedWordValid(typedWordValid).setHasMinimalSuggestion(
+                builder.setTypedWordValid(!allowsToBeAutoCorrected).setHasMinimalSuggestion(
                         autoCorrectionAvailable);
             } else {
                 SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions();
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index f6f5581..caa5aac 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -67,10 +67,10 @@
     public static final int DIC_USER_UNIGRAM = 3;
     public static final int DIC_CONTACTS = 4;
     public static final int DIC_USER_BIGRAM = 5;
+    public static final int DIC_WHITELIST = 6;
     // If you add a type of dictionary, increment DIC_TYPE_LAST_ID
     // TODO: this value seems unused. Remove it?
-    public static final int DIC_TYPE_LAST_ID = 5;
-
+    public static final int DIC_TYPE_LAST_ID = 6;
     public static final String DICT_KEY_MAIN = "main";
     public static final String DICT_KEY_CONTACTS = "contacts";
     // User dictionary, the system-managed one.
@@ -360,7 +360,7 @@
         final String typedWordString = typedWord == null ? null : typedWord.toString();
 
         CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase, mIsFirstCharCapitalized,
-                mWhiteListDictionary.getWhiteListedWord(typedWordString));
+                mWhiteListDictionary.getWhitelistedWord(typedWordString));
 
         mAutoCorrection.updateAutoCorrectionStatus(mUnigramDictionaries, wordComposer,
                 mSuggestions, mScores, typedWord, mAutoCorrectionThreshold, mCorrectionMode,
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index b177d14..005db36 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -175,6 +175,19 @@
         public CharSequence getWord(int pos) {
             return mWords.get(pos);
         }
+
+        public String toString() {
+            // Pretty-print method to help debug
+            final StringBuilder sb = new StringBuilder("StringBuilder: mTypedWordValid = "
+                    + mTypedWordValid + " ; mHasMinimalSuggestion = " + mHasMinimalSuggestion
+                    + " ; mIsPunctuationSuggestions = " + mIsPunctuationSuggestions
+                    + " --- ");
+            for (CharSequence s : mWords) {
+                sb.append(s);
+                sb.append(" ; ");
+            }
+            return sb.toString();
+        }
     }
 
     public static class SuggestedWordInfo {
diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
index 93474b6..8f349ce 100644
--- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
+++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
@@ -27,7 +27,7 @@
 import java.util.HashMap;
 import java.util.Locale;
 
-public class WhitelistDictionary extends Dictionary {
+public class WhitelistDictionary extends ExpandableDictionary {
 
     private static final boolean DBG = LatinImeLogger.sDBG;
     private static final String TAG = WhitelistDictionary.class.getSimpleName();
@@ -35,7 +35,9 @@
     private final HashMap<String, Pair<Integer, String>> mWhitelistWords =
             new HashMap<String, Pair<Integer, String>>();
 
+    // TODO: Conform to the async load contact of ExpandableDictionary
     public WhitelistDictionary(final Context context, final Locale locale) {
+        super(context, Suggest.DIC_WHITELIST);
         final Resources res = context.getResources();
         final Locale previousLocale = LocaleUtils.setSystemLocale(res, locale);
         if (context != null) {
@@ -61,6 +63,7 @@
                 if (before != null && after != null) {
                     mWhitelistWords.put(
                             before.toLowerCase(), new Pair<Integer, String>(score, after));
+                    addWord(after, score);
                 }
             }
         } catch (NumberFormatException e) {
@@ -70,27 +73,34 @@
         }
     }
 
-    public String getWhiteListedWord(String before) {
+    public String getWhitelistedWord(String before) {
         if (before == null) return null;
         final String lowerCaseBefore = before.toLowerCase();
         if(mWhitelistWords.containsKey(lowerCaseBefore)) {
             if (DBG) {
-                Log.d(TAG, "--- found whiteListedWord: " + lowerCaseBefore);
+                Log.d(TAG, "--- found whitelistedWord: " + lowerCaseBefore);
             }
             return mWhitelistWords.get(lowerCaseBefore).second;
         }
         return null;
     }
 
-    // Not used for WhitelistDictionary.  We use getWhitelistedWord() in Suggest.java instead
-    @Override
-    public void getWords(final WordComposer composer, final WordCallback callback,
-            final ProximityInfo proximityInfo) {
+    // See LatinIME#updateSuggestions. This breaks in the (queer) case that the whitelist
+    // lists that word a should autocorrect to word b, and word c would autocorrect to
+    // an upper-cased version of a. In this case, the way this return value is used would
+    // remove the first candidate when the user typed the upper-cased version of A.
+    // Example : abc -> def  and  xyz -> Abc
+    // A user typing Abc would experience it being autocorrected to something else (not
+    // necessarily def).
+    // There is no such combination in the whitelist at the time and there probably won't
+    // ever be - it doesn't make sense. But still.
+    public boolean shouldForciblyAutoCorrectFrom(CharSequence word) {
+        if (TextUtils.isEmpty(word)) return false;
+        final String correction = getWhitelistedWord(word.toString());
+        if (TextUtils.isEmpty(correction)) return false;
+        return !correction.equals(word);
     }
 
-    @Override
-    public boolean isValidWord(CharSequence word) {
-        if (TextUtils.isEmpty(word)) return false;
-        return !TextUtils.isEmpty(getWhiteListedWord(word.toString()));
-    }
+    // Leave implementation of getWords and isValidWord to the superclass.
+    // The words have been added to the ExpandableDictionary with addWord() inside initWordlist.
 }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 4d569b8..f9e6a5e 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -37,6 +37,7 @@
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SynchronouslyLoadedUserDictionary;
 import com.android.inputmethod.latin.Utils;
+import com.android.inputmethod.latin.WhitelistDictionary;
 import com.android.inputmethod.latin.WordComposer;
 
 import java.util.ArrayList;
@@ -79,6 +80,8 @@
             Collections.synchronizedMap(new TreeMap<String, DictionaryPool>());
     private Map<String, Dictionary> mUserDictionaries =
             Collections.synchronizedMap(new TreeMap<String, Dictionary>());
+    private Map<String, Dictionary> mWhitelistDictionaries =
+            Collections.synchronizedMap(new TreeMap<String, Dictionary>());
 
     // The threshold for a candidate to be offered as a suggestion.
     private double mSuggestionThreshold;
@@ -253,12 +256,17 @@
         mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>());
         final Map<String, Dictionary> oldUserDictionaries = mUserDictionaries;
         mUserDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>());
+        final Map<String, Dictionary> oldWhitelistDictionaries = mWhitelistDictionaries;
+        mWhitelistDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>());
         for (DictionaryPool pool : oldPools.values()) {
             pool.close();
         }
         for (Dictionary dict : oldUserDictionaries.values()) {
             dict.close();
         }
+        for (Dictionary dict : oldWhitelistDictionaries.values()) {
+            dict.close();
+        }
         return false;
     }
 
@@ -280,12 +288,18 @@
                 DictionaryFactory.createDictionaryFromManager(this, locale, fallbackResourceId,
                         USE_FULL_EDIT_DISTANCE_FLAG_ARRAY);
         final String localeStr = locale.toString();
-        Dictionary userDict = mUserDictionaries.get(localeStr);
-        if (null == userDict) {
-            userDict = new SynchronouslyLoadedUserDictionary(this, localeStr, true);
-            mUserDictionaries.put(localeStr, userDict);
+        Dictionary userDictionary = mUserDictionaries.get(localeStr);
+        if (null == userDictionary) {
+            userDictionary = new SynchronouslyLoadedUserDictionary(this, localeStr, true);
+            mUserDictionaries.put(localeStr, userDictionary);
         }
-        dictionaryCollection.addDictionary(userDict);
+        dictionaryCollection.addDictionary(userDictionary);
+        Dictionary whitelistDictionary = mWhitelistDictionaries.get(localeStr);
+        if (null == whitelistDictionary) {
+            whitelistDictionary = new WhitelistDictionary(this, locale);
+            mWhitelistDictionaries.put(localeStr, whitelistDictionary);
+        }
+        dictionaryCollection.addDictionary(whitelistDictionary);
         return new DictAndProximity(dictionaryCollection, proximityInfo);
     }
 
diff --git a/tests/src/com/android/inputmethod/latin/SuggestHelper.java b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
index a089335..464930f 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestHelper.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
@@ -89,7 +89,7 @@
     }
 
     public boolean isValidWord(CharSequence typed) {
-        return AutoCorrection.isValidWordForAutoCorrection(mSuggest.getUnigramDictionaries(),
+        return AutoCorrection.isValidWord(mSuggest.getUnigramDictionaries(),
                 typed, false);
     }