Merge "Preserve punctuation character after canceling auto correction" into honeycomb
diff --git a/java/res/values-xlarge/donottranslate.xml b/java/res/values-xlarge/donottranslate.xml
index 6f4e9b1..672dea5 100644
--- a/java/res/values-xlarge/donottranslate.xml
+++ b/java/res/values-xlarge/donottranslate.xml
@@ -19,5 +19,5 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!--  Default value of the visibility of the suggestion strip -->
-    <string name="prefs_suggestion_visibility_default_value" translatable="false">1</string>
+    <string name="prefs_suggestion_visibility_default_value" translatable="false">2</string>
 </resources>
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index adfec4c..11b92e7 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -35,6 +35,7 @@
     <!-- The language is never displayed if == 0, always displayed if < 0 -->
     <integer name="config_delay_before_fadeout_language_on_spacebar">-1</integer>
     <integer name="config_duration_of_fadeout_language_on_spacebar">50</integer>
+    <integer name="config_final_fadeout_percentage_of_language_on_spacebar">15</integer>
     <integer name="config_delay_before_preview">0</integer>
     <integer name="config_delay_after_preview">10</integer>
     <integer name="config_preview_fadein_anim_time">0</integer>
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 25ef847..42af8a8 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -174,12 +174,6 @@
     private void loadKeyboardInternal(int mode, int imeOptions, boolean voiceButtonEnabled,
             boolean voiceButtonOnPrimary, boolean isSymbols) {
         if (mInputView == null) return;
-        final Keyboard oldKeyboard = mInputView.getKeyboard();
-        final KeyboardId id = getKeyboardId(mode, imeOptions, isSymbols);
-        if (oldKeyboard != null && oldKeyboard.mId.equals(id))
-            return;
-
-        mInputView.setPreviewEnabled(mInputMethodService.getPopupOn());
 
         mMode = mode;
         mImeOptions = imeOptions;
@@ -188,9 +182,15 @@
         mIsSymbols = isSymbols;
         // Update the settings key state because number of enabled IMEs could have been changed
         mHasSettingsKey = getSettingsKeyMode(mPrefs, mInputMethodService);
-        makeSymbolsKeyboardIds();
+        final KeyboardId id = getKeyboardId(mode, imeOptions, isSymbols);
 
+        final Keyboard oldKeyboard = mInputView.getKeyboard();
+        if (oldKeyboard != null && oldKeyboard.mId.equals(id))
+            return;
+
+        makeSymbolsKeyboardIds();
         mCurrentId = id;
+        mInputView.setPreviewEnabled(mInputMethodService.getPopupOn());
         mInputView.setKeyboard(getKeyboard(id));
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index a952acf..b1815a1 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -298,8 +298,6 @@
 
         TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
-        LayoutInflater inflate =
-                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         int previewLayout = 0;
         int keyTextSize = 0;
 
@@ -365,7 +363,7 @@
 
         mPreviewPopup = new PopupWindow(context);
         if (previewLayout != 0) {
-            mPreviewText = (TextView) inflate.inflate(previewLayout, null);
+            mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null);
             mPreviewTextSizeLarge = (int) res.getDimension(R.dimen.key_preview_text_size_large);
             mPreviewPopup.setContentView(mPreviewText);
             mPreviewPopup.setBackgroundDrawable(null);
@@ -1080,9 +1078,7 @@
 
     private View inflateMiniKeyboardContainer(Key popupKey) {
         int popupKeyboardResId = mKeyboard.getPopupKeyboardResId();
-        LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
-        View container = inflater.inflate(mPopupLayout, null);
+        View container = LayoutInflater.from(getContext()).inflate(mPopupLayout, null);
         if (container == null)
             throw new NullPointerException();
 
@@ -1408,7 +1404,9 @@
         dismissPopupKeyboard();
         mBuffer = null;
         mCanvas = null;
+        mKeyboard = null;
         mMiniKeyboardCache.clear();
+        requestLayout();
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index db89340..888375b 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -70,7 +70,6 @@
     private static final float SPACEBAR_POPUP_MIN_RATIO = 0.4f;
     // Height in space key the language name will be drawn. (proportional to space key height)
     public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f;
-    private static final float SPACEBAR_LANGUAGE_BASELINE_WITHOUT_ICON = 0.65f;
     // If the full language name needs to be smaller than this value to be drawn on space key,
     // its short language name will be used instead.
     private static final float MINIMUM_SCALE_OF_LANGUAGE_NAME = 0.8f;
@@ -225,9 +224,12 @@
                     allowVariableTextSize);
 
             // Draw language text with shadow
-            final float baseline = height * (mSpaceIcon != null ? SPACEBAR_LANGUAGE_BASELINE
-                    : SPACEBAR_LANGUAGE_BASELINE_WITHOUT_ICON);
+            // In case there is no space icon, we will place the language text at the center of
+            // spacebar.
             final float descent = paint.descent();
+            final float textHeight = -paint.ascent() + descent;
+            final float baseline = (mSpaceIcon != null) ? height * SPACEBAR_LANGUAGE_BASELINE
+                    : height / 2 + textHeight / 2;
             paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, mSpacebarTextFadeFactor));
             canvas.drawText(language, width / 2, baseline - descent - 1, paint);
             paint.setColor(getSpacebarTextColor(mSpacebarTextColor, mSpacebarTextFadeFactor));
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index f7e6767..813f7d3 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -42,6 +42,7 @@
 
     private static final int TYPED_LETTER_MULTIPLIER = 2;
 
+    private static final BinaryDictionary sInstance = new BinaryDictionary();
     private int mDicTypeId;
     private int mNativeDict;
     private long mDictLength;
@@ -59,16 +60,24 @@
         }
     }
 
+    private BinaryDictionary() {
+    }
+
     /**
-     * Create a dictionary from a raw resource file
+     * Initialize a dictionary from a raw resource file
      * @param context application context for reading resources
      * @param resId the resource containing the raw binary dictionary
+     * @return initialized instance of BinaryDictionary
      */
-    public BinaryDictionary(Context context, int resId, int dicTypeId) {
-        if (resId != 0) {
-            loadDictionary(context, resId);
+    public static BinaryDictionary initDictionary(Context context, int resId, int dicTypeId) {
+        synchronized (sInstance) {
+            sInstance.closeInternal();
+            if (resId != 0) {
+                sInstance.loadDictionary(context, resId);
+                sInstance.mDicTypeId = dicTypeId;
+            }
         }
-        mDicTypeId = dicTypeId;
+        return sInstance;
     }
 
     private native int openNative(String sourceDir, long dictOffset, long dictSize,
@@ -104,6 +113,8 @@
     @Override
     public void getBigrams(final WordComposer codes, final CharSequence previousWord,
             final WordCallback callback, int[] nextLettersFrequencies) {
+        if (mNativeDict == 0) return;
+
         char[] chars = previousWord.toString().toCharArray();
         Arrays.fill(mOutputChars_bigrams, (char) 0);
         Arrays.fill(mFrequencies_bigrams, 0);
@@ -135,6 +146,8 @@
     @Override
     public void getWords(final WordComposer codes, final WordCallback callback,
             int[] nextLettersFrequencies) {
+        if (mNativeDict == 0) return;
+
         final int codesSize = codes.size();
         // Won't deal with really long words.
         if (codesSize > MAX_WORD_LENGTH - 1) return;
@@ -179,6 +192,10 @@
 
     @Override
     public synchronized void close() {
+        closeInternal();
+    }
+
+    private void closeInternal() {
         if (mNativeDict != 0) {
             closeNative(mNativeDict);
             mNativeDict = 0;
@@ -188,7 +205,10 @@
 
     @Override
     protected void finalize() throws Throwable {
-        close();
-        super.finalize();
+        try {
+            closeInternal();
+        } finally {
+            super.finalize();
+        }
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
index faee38e..a9f2c2c 100644
--- a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
+++ b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
@@ -107,7 +107,7 @@
         res.updateConfiguration(conf, res.getDisplayMetrics());
 
         int mainDicResId = LatinIME.getMainDictionaryResourceId(res);
-        BinaryDictionary bd = new BinaryDictionary(this, mainDicResId, Suggest.DIC_MAIN);
+        BinaryDictionary bd = BinaryDictionary.initDictionary(this, mainDicResId, Suggest.DIC_MAIN);
 
         // Is the dictionary larger than a placeholder? Arbitrarily chose a lower limit of
         // 4000-5000 words, whereas the LARGE_DICTIONARY is about 20000+ words.
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index f839a80..5766f6b 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -154,6 +154,7 @@
     private boolean mConfigSwipeDownDismissKeyboardEnabled;
     private int mConfigDelayBeforeFadeoutLanguageOnSpacebar;
     private int mConfigDurationOfFadeoutLanguageOnSpacebar;
+    private float mConfigFinalFadeoutFactorOfLanugageOnSpacebar;
 
     private int mCorrectionMode;
     private int mCommittedLength;
@@ -267,13 +268,16 @@
                 break;
             case MSG_FADEOUT_LANGUAGE_ON_SPACEBAR:
                 if (inputView != null)
-                    inputView.setSpacebarTextFadeFactor(0.5f, (LatinKeyboard)msg.obj);
+                    inputView.setSpacebarTextFadeFactor(
+                            (1.0f + mConfigFinalFadeoutFactorOfLanugageOnSpacebar) / 2,
+                            (LatinKeyboard)msg.obj);
                 sendMessageDelayed(obtainMessage(MSG_DISMISS_LANGUAGE_ON_SPACEBAR, msg.obj),
                         mConfigDurationOfFadeoutLanguageOnSpacebar);
                 break;
             case MSG_DISMISS_LANGUAGE_ON_SPACEBAR:
                 if (inputView != null)
-                    inputView.setSpacebarTextFadeFactor(0.0f, (LatinKeyboard)msg.obj);
+                    inputView.setSpacebarTextFadeFactor(
+                            mConfigFinalFadeoutFactorOfLanugageOnSpacebar, (LatinKeyboard)msg.obj);
                 break;
             }
         }
@@ -356,6 +360,8 @@
                 R.integer.config_delay_before_fadeout_language_on_spacebar);
         mConfigDurationOfFadeoutLanguageOnSpacebar = res.getInteger(
                 R.integer.config_duration_of_fadeout_language_on_spacebar);
+        mConfigFinalFadeoutFactorOfLanugageOnSpacebar = res.getInteger(
+                R.integer.config_final_fadeout_percentage_of_language_on_spacebar) / 100.0f;
 
         Utils.GCUtils.getInstance().reset();
         boolean tryGC = true;
@@ -438,6 +444,7 @@
 
     @Override
     public void onConfigurationChanged(Configuration conf) {
+        mSubtypeSwitcher.onConfigurationChanged(conf);
         // If orientation changed while predicting, commit the change
         if (conf.orientation != mOrientation) {
             InputConnection ic = getCurrentInputConnection();
@@ -608,7 +615,7 @@
         checkReCorrectionOnStart();
         inputView.setForeground(true);
 
-        mVoiceConnector.onStartInputView(mKeyboardSwitcher.getInputView().getWindowToken());
+        mVoiceConnector.onStartInputView(inputView.getWindowToken());
 
         if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
     }
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index f04f3ef..21c99fe 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -134,7 +134,7 @@
     private void updateEnabledSubtypes() {
         boolean foundCurrentSubtypeBecameDisabled = true;
         mAllEnabledSubtypesOfCurrentInputMethod = mImm.getEnabledInputMethodSubtypeList(
-                null, false);
+                null, true);
         mEnabledLanguagesOfCurrentInputMethod.clear();
         mEnabledKeyboardSubtypesOfCurrentInputMethod.clear();
         for (InputMethodSubtype ims: mAllEnabledSubtypesOfCurrentInputMethod) {
@@ -370,6 +370,23 @@
         }
     }
 
+    public void onConfigurationChanged(Configuration conf) {
+        final Locale systemLocale = conf.locale;
+        // If system configuration was changed, update all parameters.
+        if (!TextUtils.equals(systemLocale.toString(), mSystemLocale.toString())) {
+            if (mConfigUseSpacebarLanguageSwitcher) {
+                // If the system locale changes and is different from the saved
+                // locale (mSystemLocale), then reload the input locale list from the
+                // latin ime settings (shared prefs) and reset the input locale
+                // to the first one.
+                mLanguageSwitcher.loadLocales(mPrefs);
+                mLanguageSwitcher.setSystemLocale(systemLocale);
+            } else {
+                updateAllParameters();
+            }
+        }
+    }
+
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
         if (mConfigUseSpacebarLanguageSwitcher) {
             if (Settings.PREF_SELECTED_LANGUAGES.equals(key)) {
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 9ea9c2f..a8454b2 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -103,7 +103,7 @@
     private int mCorrectionMode = CORRECTION_BASIC;
 
     public Suggest(Context context, int dictionaryResId) {
-        mMainDict = new BinaryDictionary(context, dictionaryResId, DIC_MAIN);
+        mMainDict = BinaryDictionary.initDictionary(context, dictionaryResId, DIC_MAIN);
         initPool();
     }
 
@@ -127,7 +127,7 @@
     }
 
     public boolean hasMainDictionary() {
-        return mMainDict.getSize() > LARGE_DICTIONARY_THRESHOLD;
+        return mMainDict != null && mMainDict.getSize() > LARGE_DICTIONARY_THRESHOLD;
     }
 
     public int getApproxMaxWordLength() {
@@ -276,7 +276,7 @@
                     mHaveCorrection = true;
                 }
             }
-            mMainDict.getWords(wordComposer, this, mNextLettersFrequencies);
+            if (mMainDict != null) mMainDict.getWords(wordComposer, this, mNextLettersFrequencies);
             if ((mCorrectionMode == CORRECTION_FULL || mCorrectionMode == CORRECTION_FULL_BIGRAM)
                     && mSuggestions.size() > 0 && mPriorities.length > 0) {
                 // TODO: when the normalized score of the first suggestion is nearly equals to
@@ -496,7 +496,7 @@
     }
 
     public boolean isValidWord(final CharSequence word) {
-        if (word == null || word.length() == 0) {
+        if (word == null || word.length() == 0 || mMainDict == null) {
             return false;
         }
         return mMainDict.isValidWord(word)
diff --git a/java/src/com/android/inputmethod/voice/RecognitionView.java b/java/src/com/android/inputmethod/voice/RecognitionView.java
index 12d0de8..d6d0721 100644
--- a/java/src/com/android/inputmethod/voice/RecognitionView.java
+++ b/java/src/com/android/inputmethod/voice/RecognitionView.java
@@ -103,9 +103,7 @@
     public RecognitionView(Context context, OnClickListener clickListener) {
         mUiHandler = new Handler();
 
-        LayoutInflater inflater = (LayoutInflater) context.getSystemService(
-            Context.LAYOUT_INFLATER_SERVICE);
-        mView = inflater.inflate(R.layout.recognition_status, null);
+        mView = LayoutInflater.from(context).inflate(R.layout.recognition_status, null);
         ContentResolver cr = context.getContentResolver();
         mMinMicrophoneLevel = SettingsUtil.getSettingsFloat(
                 cr, SettingsUtil.LATIN_IME_MIN_MICROPHONE_LEVEL, 15.f);
diff --git a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
index 1ac4391..7154861 100644
--- a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
+++ b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.voice;
 
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.latin.EditingUtils;
 import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.LatinIME.UIHandler;
@@ -91,7 +92,7 @@
     private boolean mVoiceInputHighlighted;
 
     private InputMethodManager mImm;
-    private LatinIME mContext;
+    private LatinIME mService;
     private AlertDialog mVoiceWarningDialog;
     private VoiceInput mVoiceInput;
     private final VoiceResults mVoiceResults = new VoiceResults();
@@ -111,21 +112,19 @@
         return sInstance;
     }
 
-    private void initInternal(LatinIME context, SharedPreferences prefs, UIHandler h) {
-        mContext = context;
+    private void initInternal(LatinIME service, SharedPreferences prefs, UIHandler h) {
+        mService = service;
         mHandler = h;
-        mImm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+        mImm = (InputMethodManager) service.getSystemService(Context.INPUT_METHOD_SERVICE);
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         if (VOICE_INSTALLED) {
-            mVoiceInput = new VoiceInput(context, this);
-            mHints = new Hints(context, prefs, new Hints.Display() {
+            mVoiceInput = new VoiceInput(service, this);
+            mHints = new Hints(service, prefs, new Hints.Display() {
                 @Override
                 public void showHint(int viewResource) {
-                    LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
-                            Context.LAYOUT_INFLATER_SERVICE);
-                    View view = inflater.inflate(viewResource, null);
-                    mContext.setCandidatesView(view);
-                    mContext.setCandidatesViewShown(true);
+                    View view = LayoutInflater.from(mService).inflate(viewResource, null);
+                    mService.setCandidatesView(view);
+                    mService.setCandidatesViewShown(true);
                     mIsShowingHint = true;
                 }
               });
@@ -161,7 +160,7 @@
             mVoiceInput.flushAllTextModificationCounters();
             // send this intent AFTER logging any prior aggregated edits.
             mVoiceInput.logTextModifiedByChooseSuggestion(suggestion.toString(), index,
-                    wordSeparators, mContext.getCurrentInputConnection());
+                    wordSeparators, mService.getCurrentInputConnection());
         }
     }
 
@@ -170,7 +169,7 @@
         if (mVoiceWarningDialog != null && mVoiceWarningDialog.isShowing()) {
             return;
         }
-        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+        AlertDialog.Builder builder = new AlertDialog.Builder(mService);
         builder.setCancelable(true);
         builder.setIcon(R.drawable.ic_mic_dialog);
         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@@ -199,13 +198,13 @@
         final CharSequence message;
         if (mLocaleSupportedForVoiceInput) {
             message = TextUtils.concat(
-                    mContext.getText(R.string.voice_warning_may_not_understand), "\n\n",
-                            mContext.getText(R.string.voice_warning_how_to_turn_off));
+                    mService.getText(R.string.voice_warning_may_not_understand), "\n\n",
+                            mService.getText(R.string.voice_warning_how_to_turn_off));
         } else {
             message = TextUtils.concat(
-                    mContext.getText(R.string.voice_warning_locale_not_supported), "\n\n",
-                            mContext.getText(R.string.voice_warning_may_not_understand), "\n\n",
-                                    mContext.getText(R.string.voice_warning_how_to_turn_off));
+                    mService.getText(R.string.voice_warning_locale_not_supported), "\n\n",
+                            mService.getText(R.string.voice_warning_may_not_understand), "\n\n",
+                                    mService.getText(R.string.voice_warning_how_to_turn_off));
         }
         builder.setMessage(message);
 
@@ -296,7 +295,7 @@
     }
 
     public void showPunctuationHintIfNecessary() {
-        InputConnection ic = mContext.getCurrentInputConnection();
+        InputConnection ic = mService.getCurrentInputConnection();
         if (!mImmediatelyAfterVoiceInput && mAfterVoiceInput && ic != null) {
             if (mHints.showPunctuationHintIfNecessary(ic)) {
                 mVoiceInput.logPunctuationHintDisplayed();
@@ -364,17 +363,17 @@
     }
 
     private void revertVoiceInput() {
-        InputConnection ic = mContext.getCurrentInputConnection();
+        InputConnection ic = mService.getCurrentInputConnection();
         if (ic != null) ic.commitText("", 1);
-        mContext.updateSuggestions();
+        mService.updateSuggestions();
         mVoiceInputHighlighted = false;
     }
 
     public void commitVoiceInput() {
         if (VOICE_INSTALLED && mVoiceInputHighlighted) {
-            InputConnection ic = mContext.getCurrentInputConnection();
+            InputConnection ic = mService.getCurrentInputConnection();
             if (ic != null) ic.finishComposingText();
-            mContext.updateSuggestions();
+            mService.updateSuggestions();
             mVoiceInputHighlighted = false;
         }
     }
@@ -394,7 +393,7 @@
         if (mShowingVoiceSuggestions) {
             // Retain the replaced word in the alternatives array.
             String wordToBeReplaced = EditingUtils.getWordAtCursor(
-                    mContext.getCurrentInputConnection(), wordSeparators);
+                    mService.getCurrentInputConnection(), wordSeparators);
             if (!mWordToSuggestions.containsKey(wordToBeReplaced)) {
                 wordToBeReplaced = wordToBeReplaced.toLowerCase();
             }
@@ -438,8 +437,8 @@
                 builder.addWords(suggestions);
             }
             builder.setTypedWordValid(true).setHasMinimalSuggestion(true);
-            mContext.setSuggestions(builder.build());
-            mContext.setCandidatesViewShown(true);
+            mService.setSuggestions(builder.build());
+            mService.setCandidatesViewShown(true);
             return true;
         }
         return false;
@@ -486,15 +485,15 @@
         mAfterVoiceInput = true;
         mImmediatelyAfterVoiceInput = true;
 
-        InputConnection ic = mContext.getCurrentInputConnection();
-        if (!mContext.isFullscreenMode()) {
+        InputConnection ic = mService.getCurrentInputConnection();
+        if (!mService.isFullscreenMode()) {
             // Start listening for updates to the text from typing, etc.
             if (ic != null) {
                 ExtractedTextRequest req = new ExtractedTextRequest();
                 ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
             }
         }
-        mContext.vibrate();
+        mService.vibrate();
 
         final List<CharSequence> nBest = new ArrayList<CharSequence>();
         for (String c : mVoiceResults.candidates) {
@@ -511,7 +510,7 @@
         mHints.registerVoiceResult(bestResult);
 
         if (ic != null) ic.beginBatchEdit(); // To avoid extra updates on committing older text
-        mContext.commitTyped(ic);
+        mService.commitTyped(ic);
         EditingUtils.appendText(ic, bestResult);
         if (ic != null) ic.endBatchEdit();
 
@@ -525,15 +524,15 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                mContext.setCandidatesViewShown(false);
+                mService.setCandidatesViewShown(false);
                 mRecognizing = true;
                 View v = mVoiceInput.getView();
                 ViewParent p = v.getParent();
                 if (p != null && p instanceof ViewGroup) {
                     ((ViewGroup)p).removeView(v);
                 }
-                mContext.setInputView(v);
-                mContext.updateInputViewShown();
+                mService.setInputView(v);
+                mService.updateInputViewShown();
                 if (configChanged) {
                     mVoiceInput.onConfigurationChanged();
                 }
@@ -541,7 +540,7 @@
     }
 
     private void switchToLastInputMethod() {
-        IBinder token = mContext.getWindow().getWindow().getAttributes().token;
+        IBinder token = mService.getWindow().getWindow().getAttributes().token;
         mImm.switchToLastInputMethod(token);
     }
 
@@ -553,7 +552,7 @@
             // The user has started a voice input, so remember that in the
             // future (so we don't show the warning dialog after the first run).
             SharedPreferences.Editor editor =
-                    PreferenceManager.getDefaultSharedPreferences(mContext).edit();
+                    PreferenceManager.getDefaultSharedPreferences(mService).edit();
             editor.putBoolean(PREF_HAS_USED_VOICE_INPUT, true);
             SharedPreferencesCompat.apply(editor);
             mHasUsedVoiceInput = true;
@@ -563,14 +562,14 @@
             // The user has started a voice input from an unsupported locale, so remember that
             // in the future (so we don't show the warning dialog the next time they do this).
             SharedPreferences.Editor editor =
-                    PreferenceManager.getDefaultSharedPreferences(mContext).edit();
+                    PreferenceManager.getDefaultSharedPreferences(mService).edit();
             editor.putBoolean(PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE, true);
             SharedPreferencesCompat.apply(editor);
             mHasUsedVoiceInputUnsupportedLocale = true;
         }
 
         // Clear N-best suggestions
-        mContext.clearSuggestions();
+        mService.clearSuggestions();
 
         FieldContext context = makeFieldContext();
         mVoiceInput.startListening(context, swipe);
@@ -579,7 +578,6 @@
 
     public void startListening(final boolean swipe, IBinder token,
             final boolean configurationChanging) {
-        // TODO: remove swipe which is no longer used.
         if (VOICE_INSTALLED) {
             if (needsToShowWarningDialog()) {
                 // Calls reallyStartListening if user clicks OK, does nothing if user clicks Cancel.
@@ -601,7 +599,7 @@
         return ENABLE_VOICE_BUTTON && fieldCanDoVoice(fieldContext)
                 && !(attribute != null
                         && IME_OPTION_NO_MICROPHONE.equals(attribute.privateImeOptions))
-                && SpeechRecognizer.isRecognitionAvailable(mContext);
+                && SpeechRecognizer.isRecognitionAvailable(mService);
     }
 
     public void loadSettings(EditorInfo attribute, SharedPreferences sp) {
@@ -614,10 +612,10 @@
 
         if (VOICE_INSTALLED) {
             final String voiceMode = sp.getString(PREF_VOICE_MODE,
-                    mContext.getString(R.string.voice_mode_main));
-            mVoiceButtonEnabled = !voiceMode.equals(mContext.getString(R.string.voice_mode_off))
+                    mService.getString(R.string.voice_mode_main));
+            mVoiceButtonEnabled = !voiceMode.equals(mService.getString(R.string.voice_mode_off))
                     && shouldShowVoiceButton(makeFieldContext(), attribute);
-            mVoiceButtonOnPrimary = voiceMode.equals(mContext.getString(R.string.voice_mode_main));
+            mVoiceButtonOnPrimary = voiceMode.equals(mService.getString(R.string.voice_mode_main));
         }
     }
 
@@ -631,11 +629,10 @@
         // If IME is in voice mode, but still needs to show the voice warning dialog,
         // keep showing the warning.
         if (mSubtypeSwitcher.isVoiceMode() && token != null) {
-            if (needsToShowWarningDialog()) {
-                showVoiceWarningDialog(false, token, false);
-            } else {
-                startListening(false, token, false);
-            }
+            // Close keyboard view if it is been shown.
+            if (KeyboardSwitcher.getInstance().isInputViewShown())
+                KeyboardSwitcher.getInstance().getInputView().closing();
+            startListening(false, token, false);
         }
         // If we have no token, onAttachedToWindow will take care of showing dialog and start
         // listening.
@@ -668,7 +665,7 @@
                 // onCurrentInputMethodSubtypeChanged() will be called first. LatinIME will know
                 // that it's in keyboard mode and SubtypeSwitcher will call onCancelVoice().
                 mRecognizing = false;
-                mContext.switchToKeyboardView();
+                mService.switchToKeyboardView();
             }
         }
     }
@@ -686,8 +683,8 @@
 
     public FieldContext makeFieldContext() {
         SubtypeSwitcher switcher = SubtypeSwitcher.getInstance();
-        return new FieldContext(mContext.getCurrentInputConnection(),
-                mContext.getCurrentInputEditorInfo(), switcher.getInputLocaleStr(),
+        return new FieldContext(mService.getCurrentInputConnection(),
+                mService.getCurrentInputEditorInfo(), switcher.getInputLocaleStr(),
                 switcher.getEnabledLanguages());
     }
 
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 6e4e971..25580f4 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -89,7 +89,7 @@
         return 0;
     }
     dictBuf = malloc(sizeof(char) * dictSize);
-    if (dictBuf == NULL) {
+    if (!dictBuf) {
         LOGE("DICT: Can't allocate memory region for dictionary. errno=%d", errno);
         return 0;
     }
diff --git a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
new file mode 100644
index 0000000..004ddb6
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class SubtypeLocaleTests extends AndroidTestCase {
+    private static final String PACKAGE = LatinIME.class.getPackage().getName();
+
+    private Resources mRes;
+    private List<InputMethodSubtype> mKeyboardSubtypes;
+
+    public interface Predicator<T> {
+        public boolean evaluate(T object);
+    }
+
+    private static <T> List<T> filter(List<T> source, Predicator<? super T> predicator) {
+        final ArrayList<T> filtered = new ArrayList<T>();
+        for (final T element : source) {
+            if (predicator.evaluate(element))
+                filtered.add(element);
+        }
+        return filtered;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        final Context context = getContext();
+        mRes = context.getResources();
+
+        SubtypeLocale.init(context);
+
+        final InputMethodManager imm = (InputMethodManager) context.getSystemService(
+                Context.INPUT_METHOD_SERVICE);
+        for (final InputMethodInfo imi : imm.getInputMethodList()) {
+            if (imi.getPackageName().equals(PACKAGE)) {
+                mKeyboardSubtypes = filter(imi.getSubtypes(),
+                        new Predicator<InputMethodSubtype>() {
+                            @Override
+                            public boolean evaluate(InputMethodSubtype ims) {
+                                return ims.getMode().equals("keyboard");
+                            }
+                });
+                break;
+            }
+        }
+        assertNotNull("Can not find input method " + PACKAGE, mKeyboardSubtypes);
+        assertTrue("Can not find keyboard subtype", mKeyboardSubtypes.size() > 0);
+    }
+
+    // Copied from {@link java.junit.Assert#format(String, Object, Object)}
+    private static String format(String message, Object expected, Object actual) {
+        return message + " expected:<" + expected + "> but was:<" + actual + ">";
+    }
+
+    private String getStringWithLocale(int resId, Locale locale) {
+        final Locale savedLocale = Locale.getDefault();
+        try {
+            Locale.setDefault(locale);
+            return mRes.getString(resId);
+        } finally {
+            Locale.setDefault(savedLocale);
+        }
+    }
+
+    public void testSubtypeLocale() {
+        for (final InputMethodSubtype subtype : mKeyboardSubtypes) {
+            final String localeCode = subtype.getLocale();
+            final Locale locale = new Locale(localeCode);
+            // The locale name which will be displayed on spacebar.  For example 'English (US)' or
+            // 'Francais (Canada)'.  (c=\u008d)
+            final String displayName = SubtypeLocale.getFullDisplayName(locale);
+            // The subtype name in its locale.  For example 'English (US) Keyboard' or
+            // 'Clavier Francais (Canada)'.  (c=\u008d)
+            final String subtypeName = getStringWithLocale(subtype.getNameResId(), locale);
+            assertTrue(
+                    format("subtype display name of " + localeCode + ":", subtypeName, displayName),
+                    subtypeName.contains(displayName));
+        }
+    }
+}