Merge "Parameter tuning for typing."
diff --git a/java/src/com/android/inputmethod/event/InputTransaction.java b/java/src/com/android/inputmethod/event/InputTransaction.java
index 2e9014f..4fe9b40 100644
--- a/java/src/com/android/inputmethod/event/InputTransaction.java
+++ b/java/src/com/android/inputmethod/event/InputTransaction.java
@@ -40,6 +40,7 @@
 
     // Outputs
     private int mRequiredShiftUpdate = SHIFT_NO_UPDATE;
+    private boolean mRequiresUpdateSuggestions = false;
 
     public InputTransaction(final SettingsValues settingsValues, final Event event,
             final long timestamp, final int spaceState, final int shiftState) {
@@ -50,10 +51,34 @@
         mShiftState = shiftState;
     }
 
+    /**
+     * Indicate that this transaction requires some type of shift update.
+     * @param updateType What type of shift update this requires.
+     */
     public void requireShiftUpdate(final int updateType) {
         mRequiredShiftUpdate = Math.max(mRequiredShiftUpdate, updateType);
     }
+
+    /**
+     * Gets what type of shift update this transaction requires.
+     * @return The shift update type.
+     */
     public int getRequiredShiftUpdate() {
         return mRequiredShiftUpdate;
     }
+
+    /**
+     * Indicate that this transaction requires updating the suggestions.
+     */
+    public void setRequiresUpdateSuggestions() {
+        mRequiresUpdateSuggestions = true;
+    }
+
+    /**
+     * Find out whether this transaction requires updating the suggestions.
+     * @return Whether this transaction requires updating the suggestions.
+     */
+    public boolean requiresUpdateSuggestions() {
+        return mRequiresUpdateSuggestions;
+    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 8246c92..03425ef 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -60,7 +60,7 @@
 import com.android.inputmethod.latin.settings.DebugSettings;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.CoordinateUtils;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+import com.android.inputmethod.latin.utils.SpacebarLanguageUtils;
 import com.android.inputmethod.latin.utils.TypefaceUtils;
 import com.android.inputmethod.latin.utils.UsabilityStudyLogUtils;
 import com.android.inputmethod.research.ResearchLogger;
@@ -918,14 +918,13 @@
     // Layout language name on spacebar.
     private String layoutLanguageOnSpacebar(final Paint paint,
             final InputMethodSubtype subtype, final int width) {
-
         // Choose appropriate language name to fit into the width.
-        final String fullText = SubtypeLocaleUtils.getFullDisplayName(subtype);
+        final String fullText = SpacebarLanguageUtils.getFullDisplayName(subtype);
         if (fitsTextIntoWidth(width, fullText, paint)) {
             return fullText;
         }
 
-        final String middleText = SubtypeLocaleUtils.getMiddleDisplayName(subtype);
+        final String middleText = SpacebarLanguageUtils.getMiddleDisplayName(subtype);
         if (fitsTextIntoWidth(width, middleText, paint)) {
             return middleText;
         }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 0c0be82..53e6232 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -38,7 +38,6 @@
 import android.os.Debug;
 import android.os.IBinder;
 import android.os.Message;
-import android.os.SystemClock;
 import android.preference.PreferenceManager;
 import android.text.InputType;
 import android.text.TextUtils;
@@ -1230,7 +1229,7 @@
         final InputTransaction completeInputTransaction =
                 mInputLogic.onCodeInput(mSettings.getCurrent(), event,
                         mKeyboardSwitcher.getKeyboardShiftMode(), mHandler);
-        updateShiftModeAfterInputTransaction(completeInputTransaction.getRequiredShiftUpdate());
+        updateStateAfterInputTransaction(completeInputTransaction);
         mKeyboardSwitcher.onCodeInput(codePoint);
     }
 
@@ -1450,7 +1449,7 @@
         final InputTransaction completeInputTransaction = mInputLogic.onPickSuggestionManually(
                 mSettings.getCurrent(), index, suggestionInfo,
                 mKeyboardSwitcher.getKeyboardShiftMode(), mHandler);
-        updateShiftModeAfterInputTransaction(completeInputTransaction.getRequiredShiftUpdate());
+        updateStateAfterInputTransaction(completeInputTransaction);
     }
 
     @Override
@@ -1488,8 +1487,14 @@
         }
     }
 
-    private void updateShiftModeAfterInputTransaction(final int requiredShiftUpdate) {
-        switch (requiredShiftUpdate) {
+    /**
+     * After an input transaction has been executed, some state must be updated. This includes
+     * the shift state of the keyboard and suggestions. This method looks at the finished
+     * inputTransaction to find out what is necessary and updates the state accordingly.
+     * @param inputTransaction The transaction that has been executed.
+     */
+    private void updateStateAfterInputTransaction(final InputTransaction inputTransaction) {
+        switch (inputTransaction.getRequiredShiftUpdate()) {
         case InputTransaction.SHIFT_UPDATE_LATER:
             mHandler.postUpdateShiftState();
             break;
@@ -1498,6 +1503,9 @@
             break;
         default: // SHIFT_NO_UPDATE
         }
+        if (inputTransaction.requiresUpdateSuggestions()) {
+            mHandler.postUpdateSuggestionStrip();
+        }
     }
 
     private void hapticAndAudioFeedback(final int code, final int repeatCount) {
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index e2cdbb3..ec6bd28 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -130,8 +130,11 @@
         // so we try using some heuristics to find out about these and fix them.
         mConnection.tryFixLyingCursorPosition();
         cancelDoubleSpacePeriodCountdown();
-        mInputLogicHandler.destroy();
-        mInputLogicHandler = new InputLogicHandler(mLatinIME, this);
+        if (InputLogicHandler.NULL_HANDLER == mInputLogicHandler) {
+            mInputLogicHandler = new InputLogicHandler(mLatinIME, this);
+        } else {
+            mInputLogicHandler.reset();
+        }
     }
 
     /**
@@ -142,8 +145,7 @@
             mConnection.finishComposingText();
         }
         resetComposingState(true /* alsoResetLastComposedWord */);
-        mInputLogicHandler.destroy();
-        mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
+        mInputLogicHandler.reset();
     }
 
     /**
@@ -403,7 +405,7 @@
             // A special key, like delete, shift, emoji, or the settings key.
             switch (event.mKeyCode) {
             case Constants.CODE_DELETE:
-                handleBackspace(inputTransaction, handler);
+                handleBackspace(inputTransaction);
                 LatinImeLogger.logOnDelete(event.mX, event.mY);
                 break;
             case Constants.CODE_SHIFT:
@@ -672,7 +674,7 @@
                     commitTyped(inputTransaction.mSettingsValues, LastComposedWord.NOT_A_SEPARATOR);
                 }
             }
-            handleNonSeparator(inputTransaction.mSettingsValues, inputTransaction, handler);
+            handleNonSeparator(inputTransaction.mSettingsValues, inputTransaction);
         }
         return didAutoCorrect;
     }
@@ -683,9 +685,7 @@
      * @param inputTransaction The transaction in progress.
      */
     private void handleNonSeparator(final SettingsValues settingsValues,
-            final InputTransaction inputTransaction,
-            // TODO: Remove this argument
-            final LatinIME.UIHandler handler) {
+            final InputTransaction inputTransaction) {
         final int codePoint = inputTransaction.mEvent.mCodePoint;
         // TODO: refactor this method to stop flipping isComposingWord around all the time, and
         // make it shorter (possibly cut into several pieces). Also factor handleNonSpecialCharacter
@@ -761,7 +761,7 @@
             // In case the "add to dictionary" hint was still displayed.
             mSuggestionStripViewAccessor.dismissAddToDictionaryHint();
         }
-        handler.postUpdateSuggestionStrip();
+        inputTransaction.setRequiresUpdateSuggestions();
         if (settingsValues.mIsInternal) {
             LatinImeLoggerUtils.onNonSeparator((char)codePoint, inputTransaction.mEvent.mX,
                     inputTransaction.mEvent.mY);
@@ -843,7 +843,7 @@
             }
 
             startDoubleSpacePeriodCountdown(inputTransaction);
-            handler.postUpdateSuggestionStrip();
+            inputTransaction.setRequiresUpdateSuggestions();
         } else {
             if (swapWeakSpace) {
                 swapSwapperAndSpace(inputTransaction);
@@ -879,9 +879,7 @@
      * Handle a press on the backspace key.
      * @param inputTransaction The transaction in progress.
      */
-    private void handleBackspace(final InputTransaction inputTransaction,
-            // TODO: remove this argument
-            final LatinIME.UIHandler handler) {
+    private void handleBackspace(final InputTransaction inputTransaction) {
         mSpaceState = SpaceState.NONE;
         mDeleteCount++;
 
@@ -910,7 +908,7 @@
                 mWordComposer.deleteLast(inputTransaction.mEvent);
             }
             mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
-            handler.postUpdateSuggestionStrip();
+            inputTransaction.setRequiresUpdateSuggestions();
             if (!mWordComposer.isComposingWord()) {
                 // If we just removed the last character, auto-caps mode may have changed so we
                 // need to re-evaluate.
@@ -921,7 +919,7 @@
                 if (inputTransaction.mSettingsValues.mIsInternal) {
                     LatinImeLoggerUtils.onAutoCorrectionCancellation();
                 }
-                revertCommit(inputTransaction.mSettingsValues, handler);
+                revertCommit(inputTransaction);
                 return;
             }
             if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
@@ -1400,11 +1398,9 @@
      *
      * This is triggered upon pressing backspace just after a commit with auto-correction.
      *
-     * @param settingsValues the current settings values.
+     * @param inputTransaction The transaction in progress.
      */
-    private void revertCommit(final SettingsValues settingsValues,
-            // TODO: remove this argument
-            final LatinIME.UIHandler handler) {
+    private void revertCommit(final InputTransaction inputTransaction) {
         final String previousWord = mLastComposedWord.mPrevWord;
         final CharSequence originallyTypedWord = mLastComposedWord.mTypedWord;
         final CharSequence committedWord = mLastComposedWord.mCommittedWord;
@@ -1448,7 +1444,8 @@
                 // Given this, we add it to the list of suggestions, otherwise we discard it.
                 if (span instanceof SuggestionSpan) {
                     final SuggestionSpan suggestionSpan = (SuggestionSpan)span;
-                    if (!suggestionSpan.getLocale().equals(settingsValues.mLocale.toString())) {
+                    if (!suggestionSpan.getLocale().equals(
+                            inputTransaction.mSettingsValues.mLocale.toString())) {
                         continue;
                     }
                     for (final String suggestion : suggestionSpan.getSuggestions()) {
@@ -1463,11 +1460,11 @@
                 }
             }
             // Add the suggestion list to the list of suggestions.
-            textToCommit.setSpan(new SuggestionSpan(settingsValues.mLocale,
+            textToCommit.setSpan(new SuggestionSpan(inputTransaction.mSettingsValues.mLocale,
                     suggestions.toArray(new String[suggestions.size()]), 0 /* flags */),
                     0 /* start */, lastCharIndex /* end */, 0 /* flags */);
         }
-        if (settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) {
+        if (inputTransaction.mSettingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) {
             // For languages with spaces, we revert to the typed string, but the cursor is still
             // after the separator so we don't resume suggestions. If the user wants to correct
             // the word, they have to press backspace again.
@@ -1480,7 +1477,7 @@
                     mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), previousWord);
             mConnection.setComposingText(textToCommit, 1);
         }
-        if (settingsValues.mIsInternal) {
+        if (inputTransaction.mSettingsValues.mIsInternal) {
             LatinImeLoggerUtils.onSeparator(mLastComposedWord.mSeparatorString,
                     Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
         }
@@ -1493,7 +1490,7 @@
         // separator.
         mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
         // We have a separator between the word and the cursor: we should show predictions.
-        handler.postUpdateSuggestionStrip();
+        inputTransaction.setRequiresUpdateSuggestions();
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
index db96de3..42f0d7c 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
@@ -43,7 +43,7 @@
     // is initialized, though probably only the monkey can actually do this.
     public static final InputLogicHandler NULL_HANDLER = new InputLogicHandler() {
         @Override
-        public void destroy() {}
+        public void reset() {}
         @Override
         public boolean handleMessage(final Message msg) { return true; }
         @Override
@@ -75,8 +75,8 @@
         mInputLogic = inputLogic;
     }
 
-    public void destroy() {
-        mNonUIThreadHandler.getLooper().quit();
+    public void reset() {
+        mNonUIThreadHandler.removeCallbacksAndMessages(null);
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index a07e8eb..6a52481 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -268,6 +268,7 @@
             // if it doesn't. See documentation for binarySearch.
             final int insertIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1;
 
+            // Weak <- insertIndex == 0, ..., insertIndex == mLength -> Strong
             if (insertIndex == 0 && mLength >= mMaxLength) {
                 // In the future, we may want to keep track of the best suggestion score even if
                 // we are asked for 0 suggestions. In this case, we can use the following
@@ -285,11 +286,6 @@
                 // }
                 return true;
             }
-            if (insertIndex >= mMaxLength) {
-                // We found a suggestion, but its score is too weak to be kept considering
-                // the suggestion limit.
-                return true;
-            }
 
             final String wordString = new String(word, wordOffset, wordLength);
             if (mLength < mMaxLength) {
@@ -297,12 +293,13 @@
                 ++mLength;
                 System.arraycopy(mScores, insertIndex, mScores, insertIndex + 1, copyLen);
                 mSuggestions.add(insertIndex, wordString);
+                mScores[insertIndex] = score;
             } else {
-                System.arraycopy(mScores, 1, mScores, 0, insertIndex);
+                System.arraycopy(mScores, 1, mScores, 0, insertIndex - 1);
                 mSuggestions.add(insertIndex, wordString);
                 mSuggestions.remove(0);
+                mScores[insertIndex - 1] = score;
             }
-            mScores[insertIndex] = score;
 
             return true;
         }
diff --git a/java/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtils.java b/java/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtils.java
new file mode 100644
index 0000000..89837c6
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtils.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 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.utils;
+
+import android.view.inputmethod.InputMethodSubtype;
+
+import java.util.Locale;
+
+public final class SpacebarLanguageUtils {
+    private SpacebarLanguageUtils() {
+        // Intentional empty constructor for utility class.
+    }
+
+    // InputMethodSubtype's display name for spacebar text in its locale.
+    //        isAdditionalSubtype (T=true, F=false)
+    // locale layout  |  Middle      Full
+    // ------ ------- - --------- ----------------------
+    //  en_US qwerty  F  English   English (US)           exception
+    //  en_GB qwerty  F  English   English (UK)           exception
+    //  es_US spanish F  Español   Español (EE.UU.)       exception
+    //  fr    azerty  F  Français  Français
+    //  fr_CA qwerty  F  Français  Français (Canada)
+    //  fr_CH swiss   F  Français  Français (Suisse)
+    //  de    qwertz  F  Deutsch   Deutsch
+    //  de_CH swiss   T  Deutsch   Deutsch (Schweiz)
+    //  zz    qwerty  F  QWERTY    QWERTY
+    //  fr    qwertz  T  Français  Français
+    //  de    qwerty  T  Deutsch   Deutsch
+    //  en_US azerty  T  English   English (US)
+    //  zz    azerty  T  AZERTY    AZERTY
+    // Get InputMethodSubtype's full display name in its locale.
+    public static String getFullDisplayName(final InputMethodSubtype subtype) {
+        if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
+            return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype);
+        }
+        return SubtypeLocaleUtils.getSubtypeLocaleDisplayName(subtype.getLocale());
+    }
+
+    // Get InputMethodSubtype's middle display name in its locale.
+    public static String getMiddleDisplayName(final InputMethodSubtype subtype) {
+        if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
+            return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype);
+        }
+        final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
+        return SubtypeLocaleUtils.getSubtypeLocaleDisplayName(locale.getLanguage());
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
index 4f556f9..2452864 100644
--- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
@@ -292,41 +292,6 @@
         return keyboardLayoutSet;
     }
 
-    // InputMethodSubtype's display name for spacebar text in its locale.
-    //        isAdditionalSubtype (T=true, F=false)
-    // locale layout  |  Middle      Full
-    // ------ ------- - --------- ----------------------
-    //  en_US qwerty  F  English   English (US)           exception
-    //  en_GB qwerty  F  English   English (UK)           exception
-    //  es_US spanish F  Español   Español (EE.UU.)       exception
-    //  fr    azerty  F  Français  Français
-    //  fr_CA qwerty  F  Français  Français (Canada)
-    //  fr_CH swiss   F  Français  Français (Suisse)
-    //  de    qwertz  F  Deutsch   Deutsch
-    //  de_CH swiss   T  Deutsch   Deutsch (Schweiz)
-    //  zz    qwerty  F  QWERTY    QWERTY
-    //  fr    qwertz  T  Français  Français
-    //  de    qwerty  T  Deutsch   Deutsch
-    //  en_US azerty  T  English   English (US)
-    //  zz    azerty  T  AZERTY    AZERTY
-
-    // Get InputMethodSubtype's full display name in its locale.
-    public static String getFullDisplayName(final InputMethodSubtype subtype) {
-        if (isNoLanguage(subtype)) {
-            return getKeyboardLayoutSetDisplayName(subtype);
-        }
-        return getSubtypeLocaleDisplayName(subtype.getLocale());
-    }
-
-    // Get InputMethodSubtype's middle display name in its locale.
-    public static String getMiddleDisplayName(final InputMethodSubtype subtype) {
-        if (isNoLanguage(subtype)) {
-            return getKeyboardLayoutSetDisplayName(subtype);
-        }
-        final Locale locale = getSubtypeLocale(subtype);
-        return getSubtypeLocaleDisplayName(locale.getLanguage());
-    }
-
     // TODO: Get this information from the framework instead of maintaining here by ourselves.
     // Sorted list of known Right-To-Left language codes.
     private static final String[] SORTED_RTL_LANGUAGES = {
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index afb32d1..c1372bd 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -34,9 +34,6 @@
 # To suppress compiler warnings for unused variables/functions used for debug features etc.
 LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
 
-# For C++11
-LOCAL_CFLAGS += -std=c++11
-
 include $(LOCAL_PATH)/NativeFileList.mk
 
 LOCAL_SRC_FILES := \
diff --git a/native/jni/src/suggest/core/dicnode/dic_node.cpp b/native/jni/src/suggest/core/dicnode/dic_node.cpp
index 7385597..414dc3b 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node.cpp
+++ b/native/jni/src/suggest/core/dicnode/dic_node.cpp
@@ -24,8 +24,7 @@
           mProfiler(dicNode.mProfiler),
 #endif
           mDicNodeProperties(dicNode.mDicNodeProperties), mDicNodeState(dicNode.mDicNodeState),
-          mIsCachedForNextSuggestion(dicNode.mIsCachedForNextSuggestion), mIsUsed(dicNode.mIsUsed),
-          mReleaseListener(nullptr) {
+          mIsCachedForNextSuggestion(dicNode.mIsCachedForNextSuggestion) {
     /* empty */
 }
 
@@ -36,8 +35,6 @@
     mDicNodeProperties = dicNode.mDicNodeProperties;
     mDicNodeState = dicNode.mDicNodeState;
     mIsCachedForNextSuggestion = dicNode.mIsCachedForNextSuggestion;
-    mIsUsed = dicNode.mIsUsed;
-    mReleaseListener = dicNode.mReleaseListener;
     return *this;
 }
 
diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h
index 258aa9c..47f5ec0 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node.h
@@ -19,7 +19,6 @@
 
 #include "defines.h"
 #include "suggest/core/dicnode/dic_node_profiler.h"
-#include "suggest/core/dicnode/dic_node_release_listener.h"
 #include "suggest/core/dicnode/dic_node_utils.h"
 #include "suggest/core/dicnode/internal/dic_node_state.h"
 #include "suggest/core/dicnode/internal/dic_node_properties.h"
@@ -45,7 +44,8 @@
                             + mDicNodeState.mDicNodeStateOutput.getPrevWordsLength(), \
                     charBuf, NELEMS(charBuf)); \
             AKLOGI("#%8s, %5f, %5f, %5f, %5f, %s, %d, %5f,", header, \
-                    getSpatialDistanceForScoring(), getLanguageDistanceForScoring(), \
+                    getSpatialDistanceForScoring(), \
+                    mDicNodeState.mDicNodeStateScoring.getLanguageDistance(), \
                     getNormalizedCompoundDistance(), getRawLength(), charBuf, \
                     getInputIndex(0), getNormalizedCompoundDistanceAfterFirstWord()); \
         } while (0)
@@ -89,8 +89,7 @@
 #if DEBUG_DICT
               mProfiler(),
 #endif
-              mDicNodeProperties(), mDicNodeState(), mIsCachedForNextSuggestion(false),
-              mIsUsed(false), mReleaseListener(nullptr) {}
+              mDicNodeProperties(), mDicNodeState(), mIsCachedForNextSuggestion(false) {}
 
     DicNode(const DicNode &dicNode);
     DicNode &operator=(const DicNode &dicNode);
@@ -98,7 +97,6 @@
 
     // Init for copy
     void initByCopy(const DicNode *const dicNode) {
-        mIsUsed = true;
         mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
         mDicNodeProperties.initByCopy(&dicNode->mDicNodeProperties);
         mDicNodeState.initByCopy(&dicNode->mDicNodeState);
@@ -107,7 +105,6 @@
 
     // Init for root with prevWordPtNodePos which is used for bigram
     void initAsRoot(const int rootPtNodeArrayPos, const int prevWordPtNodePos) {
-        mIsUsed = true;
         mIsCachedForNextSuggestion = false;
         mDicNodeProperties.init(rootPtNodeArrayPos, prevWordPtNodePos);
         mDicNodeState.init();
@@ -116,7 +113,6 @@
 
     // Init for root with previous word
     void initAsRootWithPreviousWord(const DicNode *const dicNode, const int rootPtNodeArrayPos) {
-        mIsUsed = true;
         mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
         mDicNodeProperties.init(rootPtNodeArrayPos, dicNode->mDicNodeProperties.getPtNodePos());
         mDicNodeState.initAsRootWithPreviousWord(&dicNode->mDicNodeState,
@@ -125,10 +121,11 @@
     }
 
     void initAsPassingChild(DicNode *parentDicNode) {
-        mIsUsed = true;
         mIsCachedForNextSuggestion = parentDicNode->mIsCachedForNextSuggestion;
-        const int parentCodePoint = parentDicNode->getNodeTypedCodePoint();
-        mDicNodeProperties.init(&parentDicNode->mDicNodeProperties, parentCodePoint);
+        const int codePoint =
+                parentDicNode->mDicNodeState.mDicNodeStateOutput.getCurrentWordCodePointAt(
+                            parentDicNode->getNodeCodePointCount());
+        mDicNodeProperties.init(&parentDicNode->mDicNodeProperties, codePoint);
         mDicNodeState.initByCopy(&parentDicNode->mDicNodeState);
         PROF_NODE_COPY(&parentDicNode->mProfiler, mProfiler);
     }
@@ -137,7 +134,6 @@
             const int childrenPtNodeArrayPos, const int probability, const bool isTerminal,
             const bool hasChildren, const bool isBlacklistedOrNotAWord,
             const uint16_t mergedNodeCodePointCount, const int *const mergedNodeCodePoints) {
-        mIsUsed = true;
         uint16_t newDepth = static_cast<uint16_t>(dicNode->getNodeCodePointCount() + 1);
         mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
         const uint16_t newLeavingDepth = static_cast<uint16_t>(
@@ -150,17 +146,6 @@
         PROF_NODE_COPY(&dicNode->mProfiler, mProfiler);
     }
 
-    AK_FORCE_INLINE void finalize() {
-        mIsUsed = false;
-        if (mReleaseListener) {
-            mReleaseListener->onReleased(this);
-        }
-    }
-
-    bool isUsed() const {
-        return mIsUsed;
-    }
-
     bool isRoot() const {
         return getNodeCodePointCount() == 0;
     }
@@ -186,11 +171,6 @@
         mIsCachedForNextSuggestion = true;
     }
 
-    // Used to expand the node in DicNodeUtils
-    int getNodeTypedCodePoint() const {
-        return mDicNodeState.mDicNodeStateOutput.getCurrentWordCodePointAt(getNodeCodePointCount());
-    }
-
     // Check if the current word and the previous word can be considered as a valid multiple word
     // suggestion.
     bool isValidMultipleWordSuggestion() const {
@@ -261,17 +241,13 @@
     }
 
     bool isTotalInputSizeExceedingLimit() const {
-        const int prevWordsLen = mDicNodeState.mDicNodeStateOutput.getPrevWordsLength();
-        const int currentWordDepth = getNodeCodePointCount();
         // TODO: 3 can be 2? Needs to be investigated.
         // TODO: Have a const variable for 3 (or 2)
-        return prevWordsLen + currentWordDepth > MAX_WORD_LENGTH - 3;
+        return getTotalNodeCodePointCount() > MAX_WORD_LENGTH - 3;
     }
 
     void outputResult(int *dest) const {
-        const uint16_t prevWordLength = mDicNodeState.mDicNodeStateOutput.getPrevWordsLength();
-        const uint16_t currentDepth = getNodeCodePointCount();
-        memmove(dest, getOutputWordBuf(), (prevWordLength + currentDepth) * sizeof(dest[0]));
+        memmove(dest, getOutputWordBuf(), getTotalNodeCodePointCount() * sizeof(dest[0]));
         DUMP_WORD_AND_SCORE("OUTPUT");
     }
 
@@ -329,11 +305,6 @@
         return mDicNodeState.mDicNodeStateScoring.getCompoundDistance(languageWeight);
     }
 
-    // Used to commit input partially
-    int getPrevWordPtNodePos() const {
-        return mDicNodeProperties.getPrevWordTerminalPtNodePos();
-    }
-
     AK_FORCE_INLINE const int *getOutputWordBuf() const {
         return mDicNodeState.mDicNodeStateOutput.getCodePointBuf();
     }
@@ -396,10 +367,6 @@
         return mDicNodeState.mDicNodeStateScoring.getSpatialDistance();
     }
 
-    float getLanguageDistanceForScoring() const {
-        return mDicNodeState.mDicNodeStateScoring.getLanguageDistance();
-    }
-
     // For space-aware gestures, we store the normalized distance at the char index
     // that ends the first word of the suggestion. We call this the distance after
     // first word.
@@ -407,22 +374,10 @@
         return mDicNodeState.mDicNodeStateScoring.getNormalizedCompoundDistanceAfterFirstWord();
     }
 
-    float getLanguageDistanceRatePerWordForScoring() const {
-        const float langDist = getLanguageDistanceForScoring();
-        const float totalWordCount =
-                static_cast<float>(mDicNodeState.mDicNodeStateOutput.getPrevWordCount() + 1);
-        return langDist / totalWordCount;
-    }
-
     float getRawLength() const {
         return mDicNodeState.mDicNodeStateScoring.getRawLength();
     }
 
-    bool isLessThanOneErrorForScoring() const {
-        return mDicNodeState.mDicNodeStateScoring.getEditCorrectionCount()
-                + mDicNodeState.mDicNodeStateScoring.getProximityCorrectionCount() <= 1;
-    }
-
     DoubleLetterLevel getDoubleLetterLevel() const {
         return mDicNodeState.mDicNodeStateScoring.getDoubleLetterLevel();
     }
@@ -466,10 +421,6 @@
 #endif
     }
 
-    void setReleaseListener(DicNodeReleaseListener *releaseListener) {
-        mReleaseListener = releaseListener;
-    }
-
     AK_FORCE_INLINE bool compare(const DicNode *right) const {
         // Promote exact matches to prevent them from being pruned.
         const bool leftExactMatch = ErrorTypeUtils::isExactMatch(getContainedErrorTypes());
@@ -507,8 +458,6 @@
     DicNodeState mDicNodeState;
     // TODO: Remove
     bool mIsCachedForNextSuggestion;
-    bool mIsUsed;
-    DicNodeReleaseListener *mReleaseListener;
 
     AK_FORCE_INLINE int getTotalInputIndex() const {
         int index = 0;
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_pool.h b/native/jni/src/suggest/core/dicnode/dic_node_pool.h
new file mode 100644
index 0000000..a660b74
--- /dev/null
+++ b/native/jni/src/suggest/core/dicnode/dic_node_pool.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef LATINIME_DIC_NODE_POOL_H
+#define LATINIME_DIC_NODE_POOL_H
+
+#include <deque>
+#include <unordered_set>
+#include <vector>
+
+#include "defines.h"
+#include "suggest/core/dicnode/dic_node.h"
+
+namespace latinime {
+
+class DicNodePool {
+ public:
+    explicit DicNodePool(const int capacity) : mDicNodes(), mPooledDicNodes() {
+        reset(capacity);
+    }
+
+    void reset(const int capacity) {
+        if (capacity == static_cast<int>(mDicNodes.size())
+                && capacity == static_cast<int>(mPooledDicNodes.size())) {
+            // No need to reset.
+            return;
+        }
+        mDicNodes.resize(capacity);
+        mDicNodes.shrink_to_fit();
+        mPooledDicNodes.clear();
+        for (auto &dicNode : mDicNodes) {
+            mPooledDicNodes.emplace_back(&dicNode);
+        }
+    }
+
+    // Get a DicNode instance from the pool. The instance has to be returned by returnInstance().
+    DicNode *getInstance() {
+        if (mPooledDicNodes.empty()) {
+            return nullptr;
+        }
+        DicNode *const dicNode = mPooledDicNodes.back();
+        mPooledDicNodes.pop_back();
+        return dicNode;
+    }
+
+    // Return an instance that has been removed from the pool by getInstance() to the pool. The
+    // instance must not be used after returning without getInstance().
+    void placeBackInstance(DicNode *dicNode) {
+        mPooledDicNodes.emplace_back(dicNode);
+    }
+
+    void dump() const {
+        AKLOGI("\n\n\n\n\n===========================");
+        std::unordered_set<const DicNode*> usedDicNodes;
+        for (const auto &dicNode : mDicNodes) {
+            usedDicNodes.insert(&dicNode);
+        }
+        for (const auto &dicNodePtr : mPooledDicNodes) {
+            usedDicNodes.erase(dicNodePtr);
+        }
+        for (const auto &usedDicNodePtr : usedDicNodes) {
+            usedDicNodePtr->dump("DIC_NODE_POOL: ");
+        }
+        AKLOGI("===========================\n\n\n\n\n");
+    }
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodePool);
+
+    std::vector<DicNode> mDicNodes;
+    std::deque<DicNode*> mPooledDicNodes;
+};
+} // namespace latinime
+#endif // LATINIME_DIC_NODE_POOL_H
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h b/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h
index 213b1b9..7b753f2 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h
@@ -23,38 +23,30 @@
 
 #include "defines.h"
 #include "suggest/core/dicnode/dic_node.h"
-#include "suggest/core/dicnode/dic_node_release_listener.h"
+#include "suggest/core/dicnode/dic_node_pool.h"
 
 namespace latinime {
 
-class DicNodePriorityQueue : public DicNodeReleaseListener {
+class DicNodePriorityQueue {
  public:
     AK_FORCE_INLINE explicit DicNodePriorityQueue(const int capacity)
-            : mCapacity(capacity), mMaxSize(capacity), mDicNodesBuf(),
-              mUnusedNodeIndices(), mNextUnusedNodeId(0), mDicNodesQueue() {
-        mDicNodesBuf.resize(mCapacity + 1);
-        mUnusedNodeIndices.resize(mCapacity + 1);
-        clearAndResizeToCapacity();
+            : mMaxSize(capacity), mDicNodesQueue(), mDicNodePool(capacity) {
+        clear();
     }
 
     // Non virtual inline destructor -- never inherit this class
     AK_FORCE_INLINE ~DicNodePriorityQueue() {}
 
-    int getSize() const {
+    AK_FORCE_INLINE int getSize() const {
         return static_cast<int>(mDicNodesQueue.size());
     }
 
-    int getMaxSize() const {
+    AK_FORCE_INLINE int getMaxSize() const {
         return mMaxSize;
     }
 
     AK_FORCE_INLINE void setMaxSize(const int maxSize) {
-        ASSERT(maxSize <= mCapacity);
-        mMaxSize = std::min(maxSize, mCapacity);
-    }
-
-    AK_FORCE_INLINE void clearAndResizeToCapacity() {
-        clearAndResize(mCapacity);
+        mMaxSize = maxSize;
     }
 
     AK_FORCE_INLINE void clear() {
@@ -62,25 +54,32 @@
     }
 
     AK_FORCE_INLINE void clearAndResize(const int maxSize) {
-        ASSERT(maxSize <= mCapacity);
+        mMaxSize = maxSize;
         while (!mDicNodesQueue.empty()) {
             mDicNodesQueue.pop();
         }
-        setMaxSize(maxSize);
-        for (int i = 0; i < mCapacity + 1; ++i) {
-            mDicNodesBuf[i].finalize();
-            mDicNodesBuf[i].setReleaseListener(this);
-            mUnusedNodeIndices[i] = (i == mCapacity) ? NOT_A_NODE_ID : (i + 1);
+        mDicNodePool.reset(mMaxSize + 1);
+    }
+
+    AK_FORCE_INLINE void copyPush(const DicNode *const dicNode) {
+        DicNode *const pooledDicNode = newDicNode(dicNode);
+        if (!pooledDicNode) {
+            return;
         }
-        mNextUnusedNodeId = 0;
+        if (getSize() < mMaxSize) {
+            mDicNodesQueue.push(pooledDicNode);
+            return;
+        }
+        if (betterThanWorstDicNode(pooledDicNode)) {
+            mDicNodePool.placeBackInstance(mDicNodesQueue.top());
+            mDicNodesQueue.pop();
+            mDicNodesQueue.push(pooledDicNode);
+            return;
+        }
+        mDicNodePool.placeBackInstance(pooledDicNode);
     }
 
-    // Copy
-    AK_FORCE_INLINE DicNode *copyPush(const DicNode *const dicNode) {
-        return copyPush(dicNode, mMaxSize);
-    }
-
-    AK_FORCE_INLINE void copyPop(DicNode *dest) {
+    AK_FORCE_INLINE void copyPop(DicNode *const dest) {
         if (mDicNodesQueue.empty()) {
             ASSERT(false);
             return;
@@ -89,34 +88,16 @@
         if (dest) {
             DicNodeUtils::initByCopy(node, dest);
         }
-        node->finalize();
+        mDicNodePool.placeBackInstance(node);
         mDicNodesQueue.pop();
     }
 
-    void onReleased(const DicNode *dicNode) {
-        const int index = static_cast<int>(dicNode - &mDicNodesBuf[0]);
-        if (mUnusedNodeIndices[index] != NOT_A_NODE_ID) {
-            // it's already released
-            return;
-        }
-        mUnusedNodeIndices[index] = mNextUnusedNodeId;
-        mNextUnusedNodeId = index;
-        ASSERT(index >= 0 && index < (mCapacity + 1));
-    }
-
-    AK_FORCE_INLINE void dump() const {
-        AKLOGI("\n\n\n\n\n===========================");
-        for (int i = 0; i < mCapacity + 1; ++i) {
-            if (mDicNodesBuf[i].isUsed()) {
-                mDicNodesBuf[i].dump("QUEUE: ");
-            }
-        }
-        AKLOGI("===========================\n\n\n\n\n");
+    AK_FORCE_INLINE void dump() {
+        mDicNodePool.dump();
     }
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodePriorityQueue);
-    static const int NOT_A_NODE_ID = -1;
 
     AK_FORCE_INLINE static bool compareDicNode(const DicNode *const left,
             const DicNode *const right) {
@@ -124,26 +105,15 @@
     }
 
     struct DicNodeComparator {
-        bool operator ()(DicNode *left, DicNode *right) {
+        bool operator ()(const DicNode *left, const DicNode *right) const {
             return compareDicNode(left, right);
         }
     };
 
     typedef std::priority_queue<DicNode *, std::vector<DicNode *>, DicNodeComparator> DicNodesQueue;
-    const int mCapacity;
     int mMaxSize;
-    std::vector<DicNode> mDicNodesBuf; // of each element of mDicNodesBuf respectively
-    std::vector<int> mUnusedNodeIndices;
-    int mNextUnusedNodeId;
     DicNodesQueue mDicNodesQueue;
-
-    inline bool isFull(const int maxSize) const {
-        return getSize() >= maxSize;
-    }
-
-    AK_FORCE_INLINE void pop() {
-        copyPop(nullptr);
-    }
+    DicNodePool mDicNodePool;
 
     AK_FORCE_INLINE bool betterThanWorstDicNode(const DicNode *const dicNode) const {
         DicNode *worstNode = mDicNodesQueue.top();
@@ -153,61 +123,13 @@
         return compareDicNode(dicNode, worstNode);
     }
 
-    AK_FORCE_INLINE DicNode *searchEmptyDicNode() {
-        if (mCapacity == 0) {
-            return nullptr;
-        }
-        if (mNextUnusedNodeId == NOT_A_NODE_ID) {
-            AKLOGI("No unused node found.");
-            for (int i = 0; i < mCapacity + 1; ++i) {
-                AKLOGI("Dump node availability, %d, %d, %d",
-                        i, mDicNodesBuf[i].isUsed(), mUnusedNodeIndices[i]);
-            }
-            ASSERT(false);
-            return nullptr;
-        }
-        DicNode *dicNode = &mDicNodesBuf[mNextUnusedNodeId];
-        markNodeAsUsed(dicNode);
-        return dicNode;
-    }
-
-    AK_FORCE_INLINE void markNodeAsUsed(DicNode *dicNode) {
-        const int index = static_cast<int>(dicNode - &mDicNodesBuf[0]);
-        mNextUnusedNodeId = mUnusedNodeIndices[index];
-        mUnusedNodeIndices[index] = NOT_A_NODE_ID;
-        ASSERT(index >= 0 && index < (mCapacity + 1));
-    }
-
-    AK_FORCE_INLINE DicNode *pushPoolNodeWithMaxSize(DicNode *dicNode, const int maxSize) {
-        if (!dicNode) {
-            return nullptr;
-        }
-        if (!isFull(maxSize)) {
-            mDicNodesQueue.push(dicNode);
-            return dicNode;
-        }
-        if (betterThanWorstDicNode(dicNode)) {
-            pop();
-            mDicNodesQueue.push(dicNode);
-            return dicNode;
-        }
-        dicNode->finalize();
-        return nullptr;
-    }
-
-    // Copy
-    AK_FORCE_INLINE DicNode *copyPush(const DicNode *const dicNode, const int maxSize) {
-        return pushPoolNodeWithMaxSize(newDicNode(dicNode), maxSize);
-    }
-
     AK_FORCE_INLINE DicNode *newDicNode(const DicNode *const dicNode) {
-        DicNode *newNode = searchEmptyDicNode();
+        DicNode *newNode = mDicNodePool.getInstance();
         if (newNode) {
             DicNodeUtils::initByCopy(dicNode, newNode);
         }
         return newNode;
     }
-
 };
 } // namespace latinime
 #endif // LATINIME_DIC_NODE_PRIORITY_QUEUE_H
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_release_listener.h b/native/jni/src/suggest/core/dicnode/dic_node_release_listener.h
deleted file mode 100644
index c3f4329..0000000
--- a/native/jni/src/suggest/core/dicnode/dic_node_release_listener.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#ifndef LATINIME_DIC_NODE_RELEASE_LISTENER_H
-#define LATINIME_DIC_NODE_RELEASE_LISTENER_H
-
-#include "defines.h"
-
-namespace latinime {
-
-class DicNode;
-
-class DicNodeReleaseListener {
- public:
-    DicNodeReleaseListener() {}
-    virtual ~DicNodeReleaseListener() {}
-    virtual void onReleased(const DicNode *dicNode) = 0;
- private:
-    DISALLOW_COPY_AND_ASSIGN(DicNodeReleaseListener);
-};
-} // namespace latinime
-#endif // LATINIME_DIC_NODE_RELEASE_LISTENER_H
diff --git a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h
index 6b8dc8c..089d446 100644
--- a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h
+++ b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h
@@ -49,15 +49,14 @@
     AK_FORCE_INLINE void reset(const int nextActiveSize, const int terminalSize) {
         mInputIndex = 0;
         mLastCachedInputIndex = 0;
-        // We want to use the max capacity for the current active dic node queue.
-        mActiveDicNodes->clearAndResizeToCapacity();
-        // nextActiveSize is used to limit the next iteration's active dic node size.
+        // The size of current active DicNode queue doesn't have to be changed.
+        mActiveDicNodes->clear();
+        // nextActiveSize is used to limit the next iteration's active DicNode size.
         const int nextActiveSizeFittingToTheCapacity = std::min(nextActiveSize, getCacheCapacity());
         mNextActiveDicNodes->clearAndResize(nextActiveSizeFittingToTheCapacity);
         mTerminalDicNodes->clearAndResize(terminalSize);
-        // We want to use the max capacity for the cached dic nodes that will be used for the
-        // continuous suggestion.
-        mCachedDicNodesForContinuousSuggestion->clearAndResizeToCapacity();
+        // The size of cached DicNode queue doesn't have to be changed.
+        mCachedDicNodesForContinuousSuggestion->clear();
     }
 
     AK_FORCE_INLINE void continueSearch() {
@@ -95,8 +94,8 @@
         mActiveDicNodes->copyPush(dicNode);
     }
 
-    AK_FORCE_INLINE bool copyPushContinue(DicNode *dicNode) {
-        return mCachedDicNodesForContinuousSuggestion->copyPush(dicNode);
+    AK_FORCE_INLINE void copyPushContinue(DicNode *dicNode) {
+        mCachedDicNodesForContinuousSuggestion->copyPush(dicNode);
     }
 
     AK_FORCE_INLINE void copyPushNextActive(DicNode *dicNode) {
diff --git a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java
new file mode 100644
index 0000000..ff1103e
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java
@@ -0,0 +1,251 @@
+/*
+ * 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.utils;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.latin.RichInputMethodManager;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+@SmallTest
+public class SpacebarLanguagetUtilsTests extends AndroidTestCase {
+    // All input method subtypes of LatinIME.
+    private final ArrayList<InputMethodSubtype> mSubtypesList = CollectionUtils.newArrayList();
+
+    private RichInputMethodManager mRichImm;
+    private Resources mRes;
+
+    InputMethodSubtype EN_US;
+    InputMethodSubtype EN_GB;
+    InputMethodSubtype ES_US;
+    InputMethodSubtype FR;
+    InputMethodSubtype FR_CA;
+    InputMethodSubtype FR_CH;
+    InputMethodSubtype DE;
+    InputMethodSubtype DE_CH;
+    InputMethodSubtype ZZ;
+    InputMethodSubtype DE_QWERTY;
+    InputMethodSubtype FR_QWERTZ;
+    InputMethodSubtype EN_US_AZERTY;
+    InputMethodSubtype EN_UK_DVORAK;
+    InputMethodSubtype ES_US_COLEMAK;
+    InputMethodSubtype ZZ_AZERTY;
+    InputMethodSubtype ZZ_PC;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final Context context = getContext();
+        RichInputMethodManager.init(context);
+        mRichImm = RichInputMethodManager.getInstance();
+        mRes = context.getResources();
+        SubtypeLocaleUtils.init(context);
+
+        final InputMethodInfo imi = mRichImm.getInputMethodInfoOfThisIme();
+        final int subtypeCount = imi.getSubtypeCount();
+        for (int index = 0; index < subtypeCount; index++) {
+            final InputMethodSubtype subtype = imi.getSubtypeAt(index);
+            mSubtypesList.add(subtype);
+        }
+
+        EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.US.toString(), "qwerty");
+        EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.UK.toString(), "qwerty");
+        ES_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                "es_US", "spanish");
+        FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.FRENCH.toString(), "azerty");
+        FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.CANADA_FRENCH.toString(), "qwerty");
+        FR_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                "fr_CH", "swiss");
+        DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.GERMAN.toString(), "qwertz");
+        DE_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                "de_CH", "swiss");
+        ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                SubtypeLocaleUtils.NO_LANGUAGE, "qwerty");
+        DE_QWERTY = AdditionalSubtypeUtils.createAdditionalSubtype(
+                Locale.GERMAN.toString(), "qwerty", null);
+        FR_QWERTZ = AdditionalSubtypeUtils.createAdditionalSubtype(
+                Locale.FRENCH.toString(), "qwertz", null);
+        EN_US_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype(
+                Locale.US.toString(), "azerty", null);
+        EN_UK_DVORAK = AdditionalSubtypeUtils.createAdditionalSubtype(
+                Locale.UK.toString(), "dvorak", null);
+        ES_US_COLEMAK = AdditionalSubtypeUtils.createAdditionalSubtype(
+                "es_US", "colemak", null);
+        ZZ_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype(
+                SubtypeLocaleUtils.NO_LANGUAGE, "azerty", null);
+        ZZ_PC = AdditionalSubtypeUtils.createAdditionalSubtype(
+                SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty", null);
+    }
+
+    public void testAllFullDisplayNameForSpacebar() {
+        for (final InputMethodSubtype subtype : mSubtypesList) {
+            final String subtypeName = SubtypeLocaleUtils
+                    .getSubtypeDisplayNameInSystemLocale(subtype);
+            final String spacebarText = SpacebarLanguageUtils.getFullDisplayName(subtype);
+            final String languageName = SubtypeLocaleUtils
+                    .getSubtypeLocaleDisplayName(subtype.getLocale());
+            if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
+                assertFalse(subtypeName, spacebarText.contains(languageName));
+            } else {
+                assertTrue(subtypeName, spacebarText.contains(languageName));
+            }
+        }
+    }
+
+   public void testAllMiddleDisplayNameForSpacebar() {
+        for (final InputMethodSubtype subtype : mSubtypesList) {
+            final String subtypeName = SubtypeLocaleUtils
+                    .getSubtypeDisplayNameInSystemLocale(subtype);
+            final String spacebarText = SpacebarLanguageUtils.getMiddleDisplayName(subtype);
+            if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
+                assertEquals(subtypeName,
+                        SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype), spacebarText);
+            } else {
+                final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
+                assertEquals(subtypeName,
+                        SubtypeLocaleUtils.getSubtypeLocaleDisplayName(locale.getLanguage()),
+                        spacebarText);
+            }
+        }
+    }
+
+    // InputMethodSubtype's display name for spacebar text in its locale.
+    //        isAdditionalSubtype (T=true, F=false)
+    // locale layout  |  Middle     Full
+    // ------ ------- - --------- ----------------------
+    //  en_US qwerty  F  English   English (US)           exception
+    //  en_GB qwerty  F  English   English (UK)           exception
+    //  es_US spanish F  Español   Español (EE.UU.)       exception
+    //  fr    azerty  F  Français  Français
+    //  fr_CA qwerty  F  Français  Français (Canada)
+    //  fr_CH swiss   F  Français  Français (Suisse)
+    //  de    qwertz  F  Deutsch   Deutsch
+    //  de_CH swiss   F  Deutsch   Deutsch (Schweiz)
+    //  zz    qwerty  F  QWERTY    QWERTY
+    //  fr    qwertz  T  Français  Français
+    //  de    qwerty  T  Deutsch   Deutsch
+    //  en_US azerty  T  English   English (US)
+    //  zz    azerty  T  AZERTY    AZERTY
+
+    private final RunInLocale<Void> testsPredefinedSubtypesForSpacebar = new RunInLocale<Void>() {
+        @Override
+        protected Void job(final Resources res) {
+            assertEquals("en_US", "English (US)",
+                    SpacebarLanguageUtils.getFullDisplayName(EN_US));
+            assertEquals("en_GB", "English (UK)",
+                    SpacebarLanguageUtils.getFullDisplayName(EN_GB));
+            assertEquals("es_US", "Español (EE.UU.)",
+                    SpacebarLanguageUtils.getFullDisplayName(ES_US));
+            assertEquals("fr", "Français",
+                    SpacebarLanguageUtils.getFullDisplayName(FR));
+            assertEquals("fr_CA", "Français (Canada)",
+                    SpacebarLanguageUtils.getFullDisplayName(FR_CA));
+            assertEquals("fr_CH", "Français (Suisse)",
+                    SpacebarLanguageUtils.getFullDisplayName(FR_CH));
+            assertEquals("de", "Deutsch",
+                    SpacebarLanguageUtils.getFullDisplayName(DE));
+            assertEquals("de_CH", "Deutsch (Schweiz)",
+                    SpacebarLanguageUtils.getFullDisplayName(DE_CH));
+            assertEquals("zz", "QWERTY",
+                    SpacebarLanguageUtils.getFullDisplayName(ZZ));
+
+            assertEquals("en_US", "English",
+                    SpacebarLanguageUtils.getMiddleDisplayName(EN_US));
+            assertEquals("en_GB", "English",
+                    SpacebarLanguageUtils.getMiddleDisplayName(EN_GB));
+            assertEquals("es_US", "Español",
+                    SpacebarLanguageUtils.getMiddleDisplayName(ES_US));
+            assertEquals("fr", "Français",
+                    SpacebarLanguageUtils.getMiddleDisplayName(FR));
+            assertEquals("fr_CA", "Français",
+                    SpacebarLanguageUtils.getMiddleDisplayName(FR_CA));
+            assertEquals("fr_CH", "Français",
+                    SpacebarLanguageUtils.getMiddleDisplayName(FR_CH));
+            assertEquals("de", "Deutsch",
+                    SpacebarLanguageUtils.getMiddleDisplayName(DE));
+            assertEquals("de_CH", "Deutsch",
+                    SpacebarLanguageUtils.getMiddleDisplayName(DE_CH));
+            assertEquals("zz", "QWERTY",
+                    SpacebarLanguageUtils.getMiddleDisplayName(ZZ));
+            return null;
+        }
+    };
+
+    private final RunInLocale<Void> testsAdditionalSubtypesForSpacebar = new RunInLocale<Void>() {
+        @Override
+        protected Void job(final Resources res) {
+            assertEquals("fr qwertz", "Français",
+                    SpacebarLanguageUtils.getFullDisplayName(FR_QWERTZ));
+            assertEquals("de qwerty", "Deutsch",
+                    SpacebarLanguageUtils.getFullDisplayName(DE_QWERTY));
+            assertEquals("en_US azerty", "English (US)",
+                    SpacebarLanguageUtils.getFullDisplayName(EN_US_AZERTY));
+            assertEquals("en_UK dvorak", "English (UK)",
+                    SpacebarLanguageUtils.getFullDisplayName(EN_UK_DVORAK));
+            assertEquals("es_US colemak", "Español (EE.UU.)",
+                    SpacebarLanguageUtils.getFullDisplayName(ES_US_COLEMAK));
+            assertEquals("zz azerty", "AZERTY",
+                    SpacebarLanguageUtils.getFullDisplayName(ZZ_AZERTY));
+            assertEquals("zz pc", "PC",
+                    SpacebarLanguageUtils.getFullDisplayName(ZZ_PC));
+
+            assertEquals("fr qwertz", "Français",
+                    SpacebarLanguageUtils.getMiddleDisplayName(FR_QWERTZ));
+            assertEquals("de qwerty", "Deutsch",
+                    SpacebarLanguageUtils.getMiddleDisplayName(DE_QWERTY));
+            assertEquals("en_US azerty", "English",
+                    SpacebarLanguageUtils.getMiddleDisplayName(EN_US_AZERTY));
+            assertEquals("en_UK dvorak", "English",
+                    SpacebarLanguageUtils.getMiddleDisplayName(EN_UK_DVORAK));
+            assertEquals("es_US colemak", "Español",
+                    SpacebarLanguageUtils.getMiddleDisplayName(ES_US_COLEMAK));
+            assertEquals("zz azerty", "AZERTY",
+                    SpacebarLanguageUtils.getMiddleDisplayName(ZZ_AZERTY));
+            assertEquals("zz pc", "PC",
+                    SpacebarLanguageUtils.getMiddleDisplayName(ZZ_PC));
+            return null;
+        }
+    };
+
+    public void testPredefinedSubtypesForSpacebarInEnglish() {
+        testsPredefinedSubtypesForSpacebar.runInLocale(mRes, Locale.ENGLISH);
+    }
+
+    public void testAdditionalSubtypeForSpacebarInEnglish() {
+        testsAdditionalSubtypesForSpacebar.runInLocale(mRes, Locale.ENGLISH);
+    }
+
+    public void testPredefinedSubtypesForSpacebarInFrench() {
+        testsPredefinedSubtypesForSpacebar.runInLocale(mRes, Locale.FRENCH);
+    }
+
+    public void testAdditionalSubtypeForSpacebarInFrench() {
+        testsAdditionalSubtypesForSpacebar.runInLocale(mRes, Locale.FRENCH);
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
index eb8a61a..ee34590 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
@@ -123,12 +123,23 @@
         assertEquals("en_US", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(EN_US));
         assertEquals("en_GB", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(EN_GB));
         assertEquals("es_US", "spanish", SubtypeLocaleUtils.getKeyboardLayoutSetName(ES_US));
-        assertEquals("fr   ", "azerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR));
+        assertEquals("fr", "azerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR));
         assertEquals("fr_CA", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_CA));
         assertEquals("fr_CH", "swiss", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_CH));
-        assertEquals("de   ", "qwertz", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE));
+        assertEquals("de", "qwertz", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE));
         assertEquals("de_CH", "swiss", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE_CH));
-        assertEquals("zz   ", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ));
+        assertEquals("zz", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ));
+
+        assertEquals("de qwerty", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE_QWERTY));
+        assertEquals("fr qwertz", "qwertz", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_QWERTZ));
+        assertEquals("en_US azerty", "azerty",
+                SubtypeLocaleUtils.getKeyboardLayoutSetName(EN_US_AZERTY));
+        assertEquals("en_UK dvorak", "dvorak",
+                SubtypeLocaleUtils.getKeyboardLayoutSetName(EN_UK_DVORAK));
+        assertEquals("es_US colemak", "colemak",
+                SubtypeLocaleUtils.getKeyboardLayoutSetName(ES_US_COLEMAK));
+        assertEquals("zz azerty", "azerty",
+                SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ_AZERTY));
     }
 
     // InputMethodSubtype's display name in system locale (en_US).
@@ -161,17 +172,17 @@
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_GB));
                 assertEquals("es_US", "Spanish (US)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ES_US));
-                assertEquals("fr   ", "French",
+                assertEquals("fr", "French",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR));
                 assertEquals("fr_CA", "French (Canada)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CA));
                 assertEquals("fr_CH", "French (Switzerland)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CH));
-                assertEquals("de   ", "German",
+                assertEquals("de", "German",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE));
                 assertEquals("de_CH", "German (Switzerland)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH));
-                assertEquals("zz   ", "Alphabet (QWERTY)",
+                assertEquals("zz", "Alphabet (QWERTY)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ));
                 return null;
             }
@@ -183,17 +194,19 @@
         final RunInLocale<Void> tests = new RunInLocale<Void>() {
             @Override
             protected Void job(final Resources res) {
-                assertEquals("fr qwertz",    "French (QWERTZ)",
+                assertEquals("fr qwertz", "French (QWERTZ)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_QWERTZ));
-                assertEquals("de qwerty",    "German (QWERTY)",
+                assertEquals("de qwerty", "German (QWERTY)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_QWERTY));
                 assertEquals("en_US azerty", "English (US) (AZERTY)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_US_AZERTY));
-                assertEquals("en_UK dvorak", "English (UK) (Dvorak)",
+                assertEquals("en_UK dvorak","English (UK) (Dvorak)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_UK_DVORAK));
-                assertEquals("es_US colemak","Spanish (US) (Colemak)",
+                assertEquals("es_US colemak", "Spanish (US) (Colemak)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ES_US_COLEMAK));
-                assertEquals("zz pc",    "Alphabet (PC)",
+                assertEquals("zz azerty", "Alphabet (AZERTY)",
+                        SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_AZERTY));
+                assertEquals("zz pc", "Alphabet (PC)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_PC));
                 return null;
             }
@@ -231,17 +244,17 @@
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_GB));
                 assertEquals("es_US", "Espagnol (États-Unis)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ES_US));
-                assertEquals("fr   ", "Français",
+                assertEquals("fr", "Français",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR));
                 assertEquals("fr_CA", "Français (Canada)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CA));
                 assertEquals("fr_CH", "Français (Suisse)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CH));
-                assertEquals("de   ", "Allemand",
+                assertEquals("de", "Allemand",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE));
                 assertEquals("de_CH", "Allemand (Suisse)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH));
-                assertEquals("zz   ", "Alphabet latin (QWERTY)",
+                assertEquals("zz", "Alphabet latin (QWERTY)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ));
                 return null;
             }
@@ -253,17 +266,19 @@
         final RunInLocale<Void> tests = new RunInLocale<Void>() {
             @Override
             protected Void job(final Resources res) {
-                assertEquals("fr qwertz",    "Français (QWERTZ)",
+                assertEquals("fr qwertz", "Français (QWERTZ)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_QWERTZ));
-                assertEquals("de qwerty",    "Allemand (QWERTY)",
+                assertEquals("de qwerty", "Allemand (QWERTY)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_QWERTY));
                 assertEquals("en_US azerty", "Anglais (États-Unis) (AZERTY)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_US_AZERTY));
                 assertEquals("en_UK dvorak", "Anglais (Royaume-Uni) (Dvorak)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_UK_DVORAK));
-                assertEquals("es_US colemak","Espagnol (États-Unis) (Colemak)",
+                assertEquals("es_US colemak", "Espagnol (États-Unis) (Colemak)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ES_US_COLEMAK));
-                assertEquals("zz pc",    "Alphabet latin (PC)",
+                assertEquals("zz azerty", "Alphabet latin (AZERTY)",
+                        SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_AZERTY));
+                assertEquals("zz pc", "Alphabet latin (PC)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_PC));
                 return null;
             }
@@ -271,126 +286,6 @@
         tests.runInLocale(mRes, Locale.FRENCH);
     }
 
-    public void testAllFullDisplayNameForSpacebar() {
-        for (final InputMethodSubtype subtype : mSubtypesList) {
-            final String subtypeName = SubtypeLocaleUtils
-                    .getSubtypeDisplayNameInSystemLocale(subtype);
-            final String spacebarText = SubtypeLocaleUtils.getFullDisplayName(subtype);
-            final String languageName = SubtypeLocaleUtils
-                    .getSubtypeLocaleDisplayName(subtype.getLocale());
-            if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
-                assertFalse(subtypeName, spacebarText.contains(languageName));
-            } else {
-                assertTrue(subtypeName, spacebarText.contains(languageName));
-            }
-        }
-    }
-
-   public void testAllMiddleDisplayNameForSpacebar() {
-        for (final InputMethodSubtype subtype : mSubtypesList) {
-            final String subtypeName = SubtypeLocaleUtils
-                    .getSubtypeDisplayNameInSystemLocale(subtype);
-            final String spacebarText = SubtypeLocaleUtils.getMiddleDisplayName(subtype);
-            if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
-                assertEquals(subtypeName,
-                        SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype), spacebarText);
-            } else {
-                final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
-                assertEquals(subtypeName,
-                        SubtypeLocaleUtils.getSubtypeLocaleDisplayName(locale.getLanguage()),
-                        spacebarText);
-            }
-        }
-    }
-
-    // InputMethodSubtype's display name for spacebar text in its locale.
-    //        isAdditionalSubtype (T=true, F=false)
-    // locale layout  |  Middle    Full
-    // ------ ------- - --------- ----------------------
-    //  en_US qwerty  F  English   English (US)           exception
-    //  en_GB qwerty  F  English   English (UK)           exception
-    //  es_US spanish F  Español   Español (EE.UU.)       exception
-    //  fr    azerty  F  Français  Français
-    //  fr_CA qwerty  F  Français  Français (Canada)
-    //  fr_CH swiss   F  Français  Français (Suisse)
-    //  de    qwertz  F  Deutsch   Deutsch
-    //  de_CH swiss   F  Deutsch   Deutsch (Schweiz)
-    //  zz    qwerty  F  QWERTY    QWERTY
-    //  fr    qwertz  T  Français  Français
-    //  de    qwerty  T  Deutsch   Deutsch
-    //  en_US azerty  T  English   English (US)
-    //  zz    azerty  T  AZERTY    AZERTY
-
-    private final RunInLocale<Void> testsPredefinedSubtypesForSpacebar = new RunInLocale<Void>() {
-        @Override
-        protected Void job(final Resources res) {
-            assertEquals("en_US", "English (US)", SubtypeLocaleUtils.getFullDisplayName(EN_US));
-            assertEquals("en_GB", "English (UK)", SubtypeLocaleUtils.getFullDisplayName(EN_GB));
-            assertEquals("es_US", "Español (EE.UU.)",
-                    SubtypeLocaleUtils.getFullDisplayName(ES_US));
-            assertEquals("fr   ", "Français",     SubtypeLocaleUtils.getFullDisplayName(FR));
-            assertEquals("fr_CA", "Français (Canada)",
-                    SubtypeLocaleUtils.getFullDisplayName(FR_CA));
-            assertEquals("fr_CH", "Français (Suisse)",
-                    SubtypeLocaleUtils.getFullDisplayName(FR_CH));
-            assertEquals("de   ", "Deutsch",      SubtypeLocaleUtils.getFullDisplayName(DE));
-            assertEquals("de_CH", "Deutsch (Schweiz)",
-                    SubtypeLocaleUtils.getFullDisplayName(DE_CH));
-            assertEquals("zz   ", "QWERTY",       SubtypeLocaleUtils.getFullDisplayName(ZZ));
-
-            assertEquals("en_US", "English",  SubtypeLocaleUtils.getMiddleDisplayName(EN_US));
-            assertEquals("en_GB", "English",  SubtypeLocaleUtils.getMiddleDisplayName(EN_GB));
-            assertEquals("es_US", "Español",  SubtypeLocaleUtils.getMiddleDisplayName(ES_US));
-            assertEquals("fr   ", "Français", SubtypeLocaleUtils.getMiddleDisplayName(FR));
-            assertEquals("fr_CA", "Français", SubtypeLocaleUtils.getMiddleDisplayName(FR_CA));
-            assertEquals("fr_CH", "Français", SubtypeLocaleUtils.getMiddleDisplayName(FR_CH));
-            assertEquals("de   ", "Deutsch",  SubtypeLocaleUtils.getMiddleDisplayName(DE));
-            assertEquals("de_CH", "Deutsch",  SubtypeLocaleUtils.getMiddleDisplayName(DE_CH));
-            assertEquals("zz   ", "QWERTY",   SubtypeLocaleUtils.getMiddleDisplayName(ZZ));
-            return null;
-        }
-    };
-
-    private final RunInLocale<Void> testsAdditionalSubtypesForSpacebar = new RunInLocale<Void>() {
-        @Override
-        protected Void job(final Resources res) {
-            assertEquals("fr qwertz",    "Français",
-                    SubtypeLocaleUtils.getFullDisplayName(FR_QWERTZ));
-            assertEquals("de qwerty",    "Deutsch",
-                    SubtypeLocaleUtils.getFullDisplayName(DE_QWERTY));
-            assertEquals("en_US azerty", "English (US)",
-                    SubtypeLocaleUtils.getFullDisplayName(EN_US_AZERTY));
-            assertEquals("zz azerty",    "AZERTY",
-                    SubtypeLocaleUtils.getFullDisplayName(ZZ_AZERTY));
-
-            assertEquals("fr qwertz",    "Français",
-                    SubtypeLocaleUtils.getMiddleDisplayName(FR_QWERTZ));
-            assertEquals("de qwerty",    "Deutsch",
-                    SubtypeLocaleUtils.getMiddleDisplayName(DE_QWERTY));
-            assertEquals("en_US azerty", "English",
-                    SubtypeLocaleUtils.getMiddleDisplayName(EN_US_AZERTY));
-            assertEquals("zz azerty",    "AZERTY",
-                    SubtypeLocaleUtils.getMiddleDisplayName(ZZ_AZERTY));
-            return null;
-        }
-    };
-
-    public void testPredefinedSubtypesForSpacebarInEnglish() {
-        testsPredefinedSubtypesForSpacebar.runInLocale(mRes, Locale.ENGLISH);
-    }
-
-    public void testAdditionalSubtypeForSpacebarInEnglish() {
-        testsAdditionalSubtypesForSpacebar.runInLocale(mRes, Locale.ENGLISH);
-    }
-
-    public void testPredefinedSubtypesForSpacebarInFrench() {
-        testsPredefinedSubtypesForSpacebar.runInLocale(mRes, Locale.FRENCH);
-    }
-
-    public void testAdditionalSubtypeForSpacebarInFrench() {
-        testsAdditionalSubtypesForSpacebar.runInLocale(mRes, Locale.FRENCH);
-    }
-
     public void testIsRtlLanguage() {
         // Known Right-to-Left language subtypes.
         final InputMethodSubtype ARABIC = mRichImm