Merge "Treat quotes as part of the word in French."
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 6589278..f0da274 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -184,6 +184,7 @@
             <flag name="actionPrevious" value="7" />
         </attr>
         <attr name="languageCode" format="string" />
+        <attr name="countryCode" format="string" />
     </declare-styleable>
 
     <declare-styleable name="Keyboard_KeyStyle">
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardParser.java b/java/src/com/android/inputmethod/keyboard/KeyboardParser.java
index df64ad5..2632796 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardParser.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardParser.java
@@ -437,11 +437,13 @@
                     R.styleable.Keyboard_Case_imeAction, id.mImeAction);
             final boolean languageCodeMatched = matchString(a,
                     R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
+            final boolean countryCodeMatched = matchString(a,
+                    R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
             final boolean selected = modeMatched && passwordInputMatched && settingsKeyMatched
                     && voiceEnabledMatched && voiceKeyMatched && colorSchemeMatched
-                    && imeActionMatched && languageCodeMatched;
+                    && imeActionMatched && languageCodeMatched && countryCodeMatched;
 
-            if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s> %s", TAG_CASE,
+            if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE,
                     textAttr(KeyboardId.modeName(
                             a.getInt(R.styleable.Keyboard_Case_mode, -1)), "mode"),
                     textAttr(KeyboardId.colorSchemeName(
@@ -453,6 +455,7 @@
                     textAttr(KeyboardId.imeOptionsName(
                             a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"),
                     textAttr(a.getString(R.styleable.Keyboard_Case_languageCode), "languageCode"),
+                    textAttr(a.getString(R.styleable.Keyboard_Case_countryCode), "countryCode"),
                     Boolean.toString(selected)));
 
             return selected;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 3297dc3..de5e097 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -67,8 +67,6 @@
     private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache =
             new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
 
-    // TODO: clean mMode up and use mAttribute instead.
-    private int mMode = KeyboardId.MODE_TEXT; /* default value */
     private EditorInfo mAttribute;
     private boolean mIsSymbols;
     /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
@@ -124,11 +122,10 @@
         prefs.registerOnSharedPreferenceChangeListener(sInstance);
     }
 
-    private void makeSymbolsKeyboardIds() {
+    private void makeSymbolsKeyboardIds(final int mode) {
         final Locale locale = mSubtypeSwitcher.getInputLocale();
         final Resources res = mInputMethodService.getResources();
         final int orientation = res.getConfiguration().orientation;
-        final int mode = mMode;
         final int colorScheme = getColorScheme();
         final boolean hasVoiceKey = mVoiceKeyEnabled && !mVoiceButtonOnPrimary;
         // Note: This comment is only applied for phone number keyboard layout.
@@ -151,37 +148,36 @@
         return mVoiceKeyEnabled && (isSymbols != mVoiceButtonOnPrimary);
     }
 
-    public void loadKeyboard(int mode, EditorInfo attribute, boolean voiceKeyEnabled,
+    public void loadKeyboard(EditorInfo attribute, boolean voiceKeyEnabled,
             boolean voiceButtonOnPrimary) {
         mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
         try {
-            loadKeyboardInternal(mode, attribute, voiceKeyEnabled, voiceButtonOnPrimary, false);
+            loadKeyboardInternal(attribute, voiceKeyEnabled, voiceButtonOnPrimary, false);
         } catch (RuntimeException e) {
             // Get KeyboardId to record which keyboard has been failed to load.
-            final KeyboardId id = getKeyboardId(mode, attribute, false);
+            final KeyboardId id = getKeyboardId(attribute, false);
             Log.w(TAG, "loading keyboard failed: " + id, e);
             LatinImeLogger.logOnException(id.toString(), e);
         }
     }
 
-    private void loadKeyboardInternal(int mode, EditorInfo attribute, boolean voiceButtonEnabled,
+    private void loadKeyboardInternal(EditorInfo attribute, boolean voiceButtonEnabled,
             boolean voiceButtonOnPrimary, boolean isSymbols) {
         if (mInputView == null) return;
 
-        mMode = mode;
         mAttribute = attribute;
         mVoiceKeyEnabled = voiceButtonEnabled;
         mVoiceButtonOnPrimary = voiceButtonOnPrimary;
         mIsSymbols = isSymbols;
         // Update the settings key state because number of enabled IMEs could have been changed
         mHasSettingsKey = getSettingsKeyMode(mPrefs, mInputMethodService);
-        final KeyboardId id = getKeyboardId(mode, attribute, isSymbols);
+        final KeyboardId id = getKeyboardId(attribute, isSymbols);
 
         final Keyboard oldKeyboard = mInputView.getKeyboard();
         if (oldKeyboard != null && oldKeyboard.mId.equals(id))
             return;
 
-        makeSymbolsKeyboardIds();
+        makeSymbolsKeyboardIds(id.mMode);
         mCurrentId = id;
         mInputView.setPreviewEnabled(mInputMethodService.getPopupOn());
         setKeyboard(getKeyboard(id));
@@ -228,7 +224,8 @@
         return keyboard;
     }
 
-    private KeyboardId getKeyboardId(int mode, EditorInfo attribute, boolean isSymbols) {
+    private KeyboardId getKeyboardId(EditorInfo attribute, boolean isSymbols) {
+        final int mode = Utils.getKeyboardMode(attribute);
         final boolean hasVoiceKey = hasVoiceKey(isSymbols);
         final int charColorId = getColorScheme();
         final int xmlId;
@@ -265,7 +262,7 @@
     }
 
     public int getKeyboardMode() {
-        return mMode;
+        return mCurrentId != null ? mCurrentId.mMode : KeyboardId.MODE_TEXT;
     }
 
     public boolean isAlphabetMode() {
@@ -569,8 +566,7 @@
     }
 
     private void toggleKeyboardMode() {
-        loadKeyboardInternal(mMode, mAttribute, mVoiceKeyEnabled, mVoiceButtonOnPrimary,
-                !mIsSymbols);
+        loadKeyboardInternal(mAttribute, mVoiceKeyEnabled, mVoiceButtonOnPrimary, !mIsSymbols);
         if (mIsSymbols) {
             mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN;
         } else {
diff --git a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
index a9f2c2c..5587c68 100644
--- a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
+++ b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
@@ -106,7 +106,7 @@
         conf.locale = locale;
         res.updateConfiguration(conf, res.getDisplayMetrics());
 
-        int mainDicResId = LatinIME.getMainDictionaryResourceId(res);
+        int mainDicResId = Utils.getMainDictionaryResourceId(res);
         BinaryDictionary bd = BinaryDictionary.initDictionary(this, mainDicResId, Suggest.DIC_MAIN);
 
         // Is the dictionary larger than a placeholder? Arbitrarily chose a lower limit of
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index c9e7f8b..40cebb3 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -18,7 +18,6 @@
 
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardActionListener;
-import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.KeyboardView;
 import com.android.inputmethod.keyboard.LatinKeyboard;
@@ -82,11 +81,27 @@
  * Input method implementation for Qwerty'ish keyboard.
  */
 public class LatinIME extends InputMethodService implements KeyboardActionListener {
-    private static final String TAG = "LatinIME";
+    private static final String TAG = LatinIME.class.getSimpleName();
     private static final boolean PERF_DEBUG = false;
     private static final boolean TRACE = false;
     private static boolean DEBUG = LatinImeLogger.sDBG;
 
+    /**
+     * The private IME option used to indicate that no microphone should be
+     * shown for a given text field. For instance, this is specified by the
+     * search dialog when the dialog is already showing a voice search button.
+     *
+     * @deprecated Use {@link LatinIME#IME_OPTION_NO_MICROPHONE} with package name prefixed.
+     */
+    public static final String IME_OPTION_NO_MICROPHONE_COMPAT = "nm";
+
+    /**
+     * The private IME option used to indicate that no microphone should be
+     * shown for a given text field. For instance, this is specified by the
+     * search dialog when the dialog is already showing a voice search button.
+     */
+    public static final String IME_OPTION_NO_MICROPHONE = "noMicrophone";
+
     private static final int DELAY_UPDATE_SUGGESTIONS = 180;
     private static final int DELAY_UPDATE_OLD_SUGGESTIONS = 300;
     private static final int DELAY_UPDATE_SHIFT_STATE = 300;
@@ -346,7 +361,7 @@
         super.onCreate();
 
         mImm = ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE));
-        mInputMethodId = Utils.getInputMethodId(mImm, getApplicationInfo().packageName);
+        mInputMethodId = Utils.getInputMethodId(mImm, getPackageName());
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         mKeyboardSwitcher = KeyboardSwitcher.getInstance();
 
@@ -395,16 +410,6 @@
         mVoiceConnector = VoiceIMEConnector.init(this, prefs, mHandler);
     }
 
-    /**
-     * Returns a main dictionary resource id
-     * @return main dictionary resource id
-     */
-    public static int getMainDictionaryResourceId(Resources res) {
-        final String MAIN_DIC_NAME = "main";
-        String packageName = LatinIME.class.getPackage().getName();
-        return res.getIdentifier(MAIN_DIC_NAME, "raw", packageName);
-    }
-
     private void initSuggest() {
         updateAutoTextEnabled();
         String locale = mSubtypeSwitcher.getInputLocaleStr();
@@ -417,7 +422,7 @@
         mQuickFixes = isQuickFixesEnabled(prefs);
 
         final Resources res = mResources;
-        int mainDicResId = getMainDictionaryResourceId(res);
+        int mainDicResId = Utils.getMainDictionaryResourceId(res);
         mSuggest = new Suggest(this, mainDicResId);
         loadAndSetAutoCorrectionThreshold(prefs);
 
@@ -494,11 +499,6 @@
         return container;
     }
 
-    private static boolean isEmailVariation(int variation) {
-        return variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
-                || variation == InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
-    }
-
     @Override
     public void onStartInputView(EditorInfo attribute, boolean restarting) {
         final KeyboardSwitcher switcher = mKeyboardSwitcher;
@@ -522,7 +522,7 @@
         mVoiceConnector.resetVoiceStates(Utils.isPasswordInputType(attribute.inputType)
                 || Utils.isVisiblePasswordInputType(attribute.inputType));
 
-        final int mode = initializeInputAttributesAndGetMode(attribute);
+        initializeInputAttributes(attribute);
 
         inputView.closing();
         mEnteredText = null;
@@ -533,7 +533,7 @@
 
         loadSettings(attribute);
         if (mSubtypeSwitcher.isKeyboardMode()) {
-            switcher.loadKeyboard(mode, attribute,
+            switcher.loadKeyboard(attribute,
                     mVoiceConnector.isVoiceButtonEnabled(),
                     mVoiceConnector.isVoiceButtonOnPrimary());
             switcher.updateShiftState();
@@ -557,10 +557,9 @@
         if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
     }
 
-    // TODO: Separate calculating keyboard mode from initializing attributes, and make it an
-    // utility method in {@link Utils}.
-    private int initializeInputAttributesAndGetMode(EditorInfo attribute) {
-        if (attribute == null) return KeyboardId.MODE_TEXT;
+    private void initializeInputAttributes(EditorInfo attribute) {
+        if (attribute == null)
+            return;
         final int inputType = attribute.inputType;
         final int variation = inputType & InputType.TYPE_MASK_VARIATION;
         mAutoSpace = false;
@@ -569,70 +568,48 @@
         mApplicationSpecifiedCompletionOn = false;
         mApplicationSpecifiedCompletions = null;
 
-        final int mode;
-        switch (inputType & InputType.TYPE_MASK_CLASS) {
-            case InputType.TYPE_CLASS_NUMBER:
-            case InputType.TYPE_CLASS_DATETIME:
-                mode = KeyboardId.MODE_NUMBER;
-                break;
-            case InputType.TYPE_CLASS_PHONE:
-                mode = KeyboardId.MODE_PHONE;
-                break;
-            case InputType.TYPE_CLASS_TEXT:
-                mIsSettingsSuggestionStripOn = true;
-                // Make sure that passwords are not displayed in candidate view
-                if (Utils.isPasswordInputType(inputType)
-                        || Utils.isVisiblePasswordInputType(inputType)) {
-                    mIsSettingsSuggestionStripOn = false;
+        if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) {
+            mIsSettingsSuggestionStripOn = true;
+            // Make sure that passwords are not displayed in candidate view
+            if (Utils.isPasswordInputType(inputType)
+                    || Utils.isVisiblePasswordInputType(inputType)) {
+                mIsSettingsSuggestionStripOn = false;
+            }
+            if (Utils.isEmailVariation(variation)
+                    || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) {
+                mAutoSpace = false;
+            } else {
+                mAutoSpace = true;
+            }
+            if (Utils.isEmailVariation(variation)) {
+                mIsSettingsSuggestionStripOn = false;
+            } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) {
+                mIsSettingsSuggestionStripOn = false;
+            } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
+                mIsSettingsSuggestionStripOn = false;
+            } else if (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) {
+                // If it's a browser edit field and auto correct is not ON explicitly, then
+                // disable auto correction, but keep suggestions on.
+                if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) {
+                    mInputTypeNoAutoCorrect = true;
                 }
-                if (LatinIME.isEmailVariation(variation)
-                        || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) {
-                    mAutoSpace = false;
-                } else {
-                    mAutoSpace = true;
-                }
-                if (LatinIME.isEmailVariation(variation)) {
-                    mIsSettingsSuggestionStripOn = false;
-                    mode = KeyboardId.MODE_EMAIL;
-                } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) {
-                    mIsSettingsSuggestionStripOn = false;
-                    mode = KeyboardId.MODE_URL;
-                } else if (variation == InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE) {
-                    mode = KeyboardId.MODE_IM;
-                } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
-                    mIsSettingsSuggestionStripOn = false;
-                    mode = KeyboardId.MODE_TEXT;
-                } else if (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) {
-                    mode = KeyboardId.MODE_WEB;
-                    // If it's a browser edit field and auto correct is not ON explicitly, then
-                    // disable auto correction, but keep suggestions on.
-                    if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) {
-                        mInputTypeNoAutoCorrect = true;
-                    }
-                } else {
-                    mode = KeyboardId.MODE_TEXT;
-                }
+            }
 
-                // If NO_SUGGESTIONS is set, don't do prediction.
-                if ((inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) {
-                    mIsSettingsSuggestionStripOn = false;
-                    mInputTypeNoAutoCorrect = true;
-                }
-                // If it's not multiline and the autoCorrect flag is not set, then don't correct
-                if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0 &&
-                        (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0) {
-                    mInputTypeNoAutoCorrect = true;
-                }
-                if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
-                    mIsSettingsSuggestionStripOn = false;
-                    mApplicationSpecifiedCompletionOn = isFullscreenMode();
-                }
-                break;
-            default:
-                mode = KeyboardId.MODE_TEXT;
-                break;
+            // If NO_SUGGESTIONS is set, don't do prediction.
+            if ((inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) {
+                mIsSettingsSuggestionStripOn = false;
+                mInputTypeNoAutoCorrect = true;
+            }
+            // If it's not multiline and the autoCorrect flag is not set, then don't correct
+            if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0
+                    && (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0) {
+                mInputTypeNoAutoCorrect = true;
+            }
+            if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
+                mIsSettingsSuggestionStripOn = false;
+                mApplicationSpecifiedCompletionOn = isFullscreenMode();
+            }
         }
-        return mode;
     }
 
     private void checkReCorrectionOnStart() {
@@ -1909,9 +1886,7 @@
             mSubtypeSwitcher.toggleLanguage(reset, next);
         }
         // Reload keyboard because the current language has been changed.
-        final EditorInfo attribute = getCurrentInputEditorInfo();
-        final int mode = initializeInputAttributesAndGetMode(attribute);
-        mKeyboardSwitcher.loadKeyboard(mode, attribute,
+        mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(),
                 mVoiceConnector.isVoiceButtonEnabled(), mVoiceConnector.isVoiceButtonOnPrimary());
         initSuggest();
         mKeyboardSwitcher.updateShiftState();
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index ee9dab3..c43f5b5 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -18,7 +18,6 @@
 
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.LatinKeyboard;
-import com.android.inputmethod.keyboard.LatinKeyboardView;
 import com.android.inputmethod.voice.SettingsUtil;
 import com.android.inputmethod.voice.VoiceIMEConnector;
 import com.android.inputmethod.voice.VoiceInput;
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 149c5ca..f267667 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -18,6 +18,7 @@
 
 import com.android.inputmethod.keyboard.KeyboardId;
 
+import android.content.res.Resources;
 import android.inputmethodservice.InputMethodService;
 import android.os.AsyncTask;
 import android.os.Handler;
@@ -45,6 +46,10 @@
     private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4;
     private static boolean DBG = LatinImeLogger.sDBG;
 
+    private Utils() {
+        // Intentional empty constructor for utility class.
+    }
+
     /**
      * Cancel an {@link AsyncTask}.
      *
@@ -319,7 +324,7 @@
     }
 
     public static class UsabilityStudyLogUtils {
-        private static final String TAG = "UsabilityStudyLogUtils";
+        private static final String USABILITY_TAG = UsabilityStudyLogUtils.class.getSimpleName();
         private static final String FILENAME = "log.txt";
         private static final UsabilityStudyLogUtils sInstance =
                 new UsabilityStudyLogUtils();
@@ -356,7 +361,7 @@
                 try {
                     mWriter = getPrintWriter(mDirectory, FILENAME, false);
                 } catch (IOException e) {
-                    Log.e(TAG, "Can't create log file.");
+                    Log.e(USABILITY_TAG, "Can't create log file.");
                 }
             }
         }
@@ -393,7 +398,7 @@
                     final String printString = String.format("%s\t%d\t%s\n",
                             mDateFormat.format(mDate), currentTime, log);
                     if (LatinImeLogger.sDBG) {
-                        Log.d(TAG, "Write: " + log);
+                        Log.d(USABILITY_TAG, "Write: " + log);
                     }
                     mWriter.print(printString);
                 }
@@ -414,10 +419,10 @@
                             sb.append(line);
                         }
                     } catch (IOException e) {
-                        Log.e(TAG, "Can't read log file.");
+                        Log.e(USABILITY_TAG, "Can't read log file.");
                     } finally {
                         if (LatinImeLogger.sDBG) {
-                            Log.d(TAG, "output all logs\n" + sb.toString());
+                            Log.d(USABILITY_TAG, "output all logs\n" + sb.toString());
                         }
                         mIms.getCurrentInputConnection().commitText(sb.toString(), 0);
                         try {
@@ -436,7 +441,7 @@
                 public void run() {
                     if (mFile != null && mFile.exists()) {
                         if (LatinImeLogger.sDBG) {
-                            Log.d(TAG, "Delete log file.");
+                            Log.d(USABILITY_TAG, "Delete log file.");
                         }
                         mFile.delete();
                         mWriter.close();
@@ -466,6 +471,43 @@
         }
     }
 
+    public static int getKeyboardMode(EditorInfo attribute) {
+        if (attribute == null)
+            return KeyboardId.MODE_TEXT;
+
+        final int inputType = attribute.inputType;
+        final int variation = inputType & InputType.TYPE_MASK_VARIATION;
+
+        switch (inputType & InputType.TYPE_MASK_CLASS) {
+        case InputType.TYPE_CLASS_NUMBER:
+        case InputType.TYPE_CLASS_DATETIME:
+            return KeyboardId.MODE_NUMBER;
+        case InputType.TYPE_CLASS_PHONE:
+            return KeyboardId.MODE_PHONE;
+        case InputType.TYPE_CLASS_TEXT:
+            if (Utils.isEmailVariation(variation)) {
+                return KeyboardId.MODE_EMAIL;
+            } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) {
+                return KeyboardId.MODE_URL;
+            } else if (variation == InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE) {
+                return KeyboardId.MODE_IM;
+            } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
+                return KeyboardId.MODE_TEXT;
+            } else if (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) {
+                return KeyboardId.MODE_WEB;
+            } else {
+                return KeyboardId.MODE_TEXT;
+            }
+        default:
+            return KeyboardId.MODE_TEXT;
+        }
+    }
+
+    public static boolean isEmailVariation(int variation) {
+        return variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
+                || variation == InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
+    }
+
     // Please refer to TextView.isPasswordInputType
     public static boolean isPasswordInputType(int inputType) {
         final int variation =
@@ -485,4 +527,30 @@
         return variation
                 == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
     }
+
+    public static boolean containsInCsv(String key, String csv) {
+        if (csv == null)
+            return false;
+        for (String option : csv.split(",")) {
+            if (option.equals(key))
+                return true;
+        }
+        return false;
+    }
+
+    public static boolean inPrivateImeOptions(String packageName, String key,
+            EditorInfo attribute) {
+        if (attribute == null)
+            return false;
+        return containsInCsv(packageName != null ? packageName + "." + key : key,
+                attribute.privateImeOptions);
+    }
+
+    /**
+     * Returns a main dictionary resource id
+     * @return main dictionary resource id
+     */
+    public static int getMainDictionaryResourceId(Resources res) {
+        return res.getIdentifier("main", "raw", LatinIME.class.getPackage().getName());
+    }
 }
diff --git a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
index 277ef7e..105656f 100644
--- a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
+++ b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
@@ -25,6 +25,7 @@
 import com.android.inputmethod.latin.SharedPreferencesCompat;
 import com.android.inputmethod.latin.SubtypeSwitcher;
 import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.Utils;
 
 import android.app.AlertDialog;
 import android.content.Context;
@@ -74,10 +75,6 @@
     // For example, the user has a Chinese UI but activates voice input.
     private static final String PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE =
             "has_used_voice_input_unsupported_locale";
-    // The private IME option used to indicate that no microphone should be shown for a
-    // given text field. For instance this is specified by the search dialog when the
-    // dialog is already showing a voice search button.
-    private static final String IME_OPTION_NO_MICROPHONE = "nm";
     private static final int RECOGNITIONVIEW_HEIGHT_THRESHOLD_RATIO = 6;
 
     private static final String TAG = VoiceIMEConnector.class.getSimpleName();
@@ -627,9 +624,11 @@
     }
 
     private boolean shouldShowVoiceButton(FieldContext fieldContext, EditorInfo attribute) {
-        return ENABLE_VOICE_BUTTON && fieldCanDoVoice(fieldContext)
-                && !(attribute != null
-                        && IME_OPTION_NO_MICROPHONE.equals(attribute.privateImeOptions))
+        final boolean noMic = Utils.inPrivateImeOptions(null,
+                LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, attribute)
+                || Utils.inPrivateImeOptions(mService.getPackageName(),
+                        LatinIME.IME_OPTION_NO_MICROPHONE, attribute);
+        return ENABLE_VOICE_BUTTON && fieldCanDoVoice(fieldContext) && !noMic
                 && SpeechRecognizer.isRecognitionAvailable(mService);
     }