Merge "Use the WordComposer to check if we are composing a word"
diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml
index 2e0cddc..b9451f8 100644
--- a/java/res/layout/input_view.xml
+++ b/java/res/layout/input_view.xml
@@ -43,7 +43,7 @@
             android:layout_width="@dimen/suggestions_strip_padding"
             android:layout_height="@dimen/suggestions_strip_height"
             style="?attr/suggestionsStripBackgroundStyle" />
-        <com.android.inputmethod.latin.SuggestionsView
+        <com.android.inputmethod.latin.suggestions.SuggestionsView
             android:id="@+id/suggestions_view"
             android:layout_weight="1.0"
             android:layout_width="0dp"
diff --git a/java/res/layout/more_suggestions.xml b/java/res/layout/more_suggestions.xml
index 6aa82e1..1e59b89 100644
--- a/java/res/layout/more_suggestions.xml
+++ b/java/res/layout/more_suggestions.xml
@@ -24,7 +24,7 @@
         android:orientation="horizontal"
         style="?attr/miniKeyboardPanelStyle"
         >
-    <com.android.inputmethod.latin.MoreSuggestionsView
+    <com.android.inputmethod.latin.suggestions.MoreSuggestionsView
             xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
             android:id="@+id/more_suggestions_view"
             android:layout_alignParentBottom="true"
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index 3b3ff07..d95c3b3 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -60,8 +60,6 @@
     public final int mOrientation;
     public final int mWidth;
     public final int mMode;
-    // TODO: Remove this field.
-    private final int mXmlId;
     public final int mElementState;
     private final int mInputType;
     private final int mImeOptions;
@@ -72,23 +70,13 @@
 
     private final int mHashCode;
 
-    public KeyboardId(int xmlId, int elementState, Locale locale, int orientation, int width,
-            int mode, EditorInfo editorInfo, boolean settingsKeyEnabled,
-            boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) {
-        this(xmlId, elementState, locale, orientation, width, mode,
-                (editorInfo != null ? editorInfo.inputType : 0),
-                (editorInfo != null ? editorInfo.imeOptions : 0),
-                settingsKeyEnabled, clobberSettingsKey, shortcutKeyEnabled, hasShortcutKey);
-    }
-
-    private KeyboardId(int xmlId, int elementState, Locale locale, int orientation, int width,
-            int mode, int inputType, int imeOptions, boolean settingsKeyEnabled,
-            boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) {
+    public KeyboardId(int elementState, Locale locale, int orientation, int width, int mode,
+            int inputType, int imeOptions, boolean settingsKeyEnabled, boolean clobberSettingsKey,
+            boolean shortcutKeyEnabled, boolean hasShortcutKey) {
         this.mLocale = locale;
         this.mOrientation = orientation;
         this.mWidth = width;
         this.mMode = mode;
-        this.mXmlId = xmlId;
         this.mElementState = elementState;
         this.mInputType = inputType;
         this.mImeOptions = imeOptions;
@@ -106,7 +94,6 @@
                 id.mElementState,
                 id.mMode,
                 id.mWidth,
-                id.mXmlId,
                 id.navigateAction(),
                 id.passwordInput(),
                 id.mSettingsKeyEnabled,
@@ -125,7 +112,6 @@
                 && other.mElementState == this.mElementState
                 && other.mMode == this.mMode
                 && other.mWidth == this.mWidth
-                && other.mXmlId == this.mXmlId
                 && other.navigateAction() == this.navigateAction()
                 && other.passwordInput() == this.passwordInput()
                 && other.mSettingsKeyEnabled == this.mSettingsKeyEnabled
@@ -136,16 +122,6 @@
                 && other.mLocale.equals(this.mLocale);
     }
 
-    public KeyboardId cloneWithNewXml(int xmlId) {
-        return new KeyboardId(xmlId, mElementState, mLocale, mOrientation, mWidth, mMode,
-                mInputType, mImeOptions, false, false, false, false);
-    }
-
-    // Remove this method.
-    public int getXmlId() {
-        return mXmlId;
-    }
-
     public boolean isAlphabetKeyboard() {
         return mElementState < ELEMENT_SYMBOLS;
     }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
index a28cfa8..e15ce06 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
@@ -17,16 +17,16 @@
 package com.android.inputmethod.keyboard;
 
 import android.content.Context;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.Xml;
 import android.view.inputmethod.EditorInfo;
 
 import com.android.inputmethod.keyboard.internal.XmlParseUtils;
 import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.LocaleUtils;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SettingsValues;
@@ -37,74 +37,157 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.lang.ref.SoftReference;
 import java.util.HashMap;
 import java.util.Locale;
 
 /**
- * This class has a set of {@link KeyboardId}s. Each of them represents a different keyboard
+ * This class represents a set of keyboards. Each of them represents a different keyboard
  * specific to a keyboard state, such as alphabet, symbols, and so on.  Layouts in the same
  * {@link KeyboardSet} are related to each other.
  * A {@link KeyboardSet} needs to be created for each {@link android.view.inputmethod.EditorInfo}.
  */
 public class KeyboardSet {
-    private static final String TAG_KEYBOARD_SET = "KeyboardSet";
+    private static final String TAG = KeyboardSet.class.getSimpleName();
+    private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG;
+
+    private static final String TAG_KEYBOARD_SET = TAG;
     private static final String TAG_ELEMENT = "Element";
 
-    // TODO: Make these KeyboardId private.
-    public final KeyboardId mAlphabetId;
-    public final KeyboardId mSymbolsId;
-    public final KeyboardId mSymbolsShiftedId;
+    private final Context mContext;
+    private final Params mParams;
 
-    KeyboardSet(Builder builder) {
-        mAlphabetId = builder.getKeyboardId(false, false);
-        mSymbolsId = builder.getKeyboardId(true, false);
-        mSymbolsShiftedId = builder.getKeyboardId(true, true);
+    private static class Params {
+        int mMode;
+        int mInputType;
+        int mImeOptions;
+        boolean mSettingsKeyEnabled;
+        boolean mVoiceKeyEnabled;
+        boolean mVoiceKeyOnMain;
+        boolean mNoSettingsKey;
+        Locale mLocale;
+        int mOrientation;
+        int mWidth;
+        final HashMap<Integer, Integer> mElementKeyboards = new HashMap<Integer, Integer>();
+        Params() {}
+    }
+
+    private static final HashMap<KeyboardId, SoftReference<LatinKeyboard>> sKeyboardCache =
+            new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
+
+    public static void clearKeyboardCache() {
+        sKeyboardCache.clear();
+    }
+
+    private KeyboardSet(Context context, Params params) {
+        mContext = context;
+        mParams = params;
+    }
+
+    public LatinKeyboard getMainKeyboard() {
+        return getKeyboard(false, false);
+    }
+
+    public LatinKeyboard getSymbolsKeyboard() {
+        return getKeyboard(true, false);
+    }
+
+    public LatinKeyboard getSymbolsShiftedKeyboard() {
+        final LatinKeyboard keyboard = getKeyboard(true, true);
+        // TODO: Remove this logic once we introduce initial keyboard shift state attribute.
+        // Symbol shift keyboard may have a shift key that has a caps lock style indicator (a.k.a.
+        // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked()
+        // that takes care of the current keyboard having such shift key or not.
+        keyboard.setShiftLocked(keyboard.hasShiftLockKey());
+        return keyboard;
+    }
+
+    private LatinKeyboard getKeyboard(boolean isSymbols, boolean isShift) {
+        final int elementState = Builder.getElementState(mParams.mMode, isSymbols, isShift);
+        final int xmlId = mParams.mElementKeyboards.get(elementState);
+        final KeyboardId id = Builder.getKeyboardId(elementState, isSymbols, mParams);
+        final LatinKeyboard keyboard = getKeyboard(mContext, xmlId, id);
+        return keyboard;
+    }
+
+    public KeyboardId getMainKeyboardId() {
+        final int elementState = Builder.getElementState(mParams.mMode, false, false);
+        return Builder.getKeyboardId(elementState, false, mParams);
+    }
+
+    private static LatinKeyboard getKeyboard(Context context, int xmlId, KeyboardId id) {
+        final Resources res = context.getResources();
+        final SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
+        final SoftReference<LatinKeyboard> ref = sKeyboardCache.get(id);
+        LatinKeyboard keyboard = (ref == null) ? null : ref.get();
+        if (keyboard == null) {
+            final Locale savedLocale = LocaleUtils.setSystemLocale(res, id.mLocale);
+            try {
+                final LatinKeyboard.Builder builder = new LatinKeyboard.Builder(context);
+                builder.load(xmlId, id);
+                builder.setTouchPositionCorrectionEnabled(
+                        subtypeSwitcher.currentSubtypeContainsExtraValueKey(
+                                LatinIME.SUBTYPE_EXTRA_VALUE_SUPPORT_TOUCH_POSITION_CORRECTION));
+                keyboard = builder.build();
+            } finally {
+                LocaleUtils.setSystemLocale(res, savedLocale);
+            }
+            sKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard));
+
+            if (DEBUG_CACHE) {
+                Log.d(TAG, "keyboard cache size=" + sKeyboardCache.size() + ": "
+                        + ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
+            }
+        } else if (DEBUG_CACHE) {
+            Log.d(TAG, "keyboard cache size=" + sKeyboardCache.size() + ": HIT  id=" + id);
+        }
+
+        // TODO: Remove setShiftLocked and setShift calls.
+        keyboard.setShiftLocked(false);
+        keyboard.setShifted(false);
+        return keyboard;
     }
 
     public static class Builder {
+        private final Context mContext;
         private final Resources mResources;
-        private final EditorInfo mEditorInfo;
 
-        private final HashMap<Integer, Integer> mElementKeyboards =
-                new HashMap<Integer, Integer>();
-
-        private final int mMode;
-        private final boolean mSettingsKeyEnabled;
-        private final boolean mVoiceKeyEnabled;
-        private final boolean mVoiceKeyOnMain;
-        private final boolean mNoSettingsKey;
-        private final Locale mLocale;
-        private final Configuration mConf;
-        private final DisplayMetrics mMetrics;
+        private final Params mParams = new Params();
 
         public Builder(Context context, EditorInfo editorInfo, SettingsValues settingsValues) {
+            mContext = context;
             mResources = context.getResources();
-            mEditorInfo = editorInfo;
             final SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
             final String packageName = context.getPackageName();
+            final Params params = mParams;
 
-            mMode = Utils.getKeyboardMode(mEditorInfo);
-            mSettingsKeyEnabled = settingsValues.isSettingsKeyEnabled();
+            params.mMode = Utils.getKeyboardMode(editorInfo);
+            if (editorInfo != null) {
+                params.mInputType = editorInfo.inputType;
+                params.mImeOptions = editorInfo.imeOptions;
+            }
+            params.mSettingsKeyEnabled = settingsValues.isSettingsKeyEnabled();
             @SuppressWarnings("deprecation")
             final boolean noMicrophone = Utils.inPrivateImeOptions(
                     packageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo)
                     || Utils.inPrivateImeOptions(
                             null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo);
-            mVoiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo) && !noMicrophone;
-            mVoiceKeyOnMain = settingsValues.isVoiceKeyOnMain();
-            mNoSettingsKey = Utils.inPrivateImeOptions(
+            params.mVoiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo) && !noMicrophone;
+            params.mVoiceKeyOnMain = settingsValues.isVoiceKeyOnMain();
+            params.mNoSettingsKey = Utils.inPrivateImeOptions(
                     packageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo);
             final boolean forceAscii = Utils.inPrivateImeOptions(
                     packageName, LatinIME.IME_OPTION_FORCE_ASCII, editorInfo);
             final boolean asciiCapable = subtypeSwitcher.currentSubtypeContainsExtraValueKey(
                     LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE);
-            mLocale = (forceAscii && !asciiCapable) ? Locale.US : subtypeSwitcher.getInputLocale();
-            mConf = mResources.getConfiguration();
-            mMetrics = mResources.getDisplayMetrics();
+            params.mLocale = (forceAscii && !asciiCapable)
+                    ? Locale.US : subtypeSwitcher.getInputLocale();
+            params.mOrientation = mResources.getConfiguration().orientation;
+            params.mWidth = mResources.getDisplayMetrics().widthPixels;
         }
 
         public KeyboardSet build() {
-            final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, mLocale);
+            final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, mParams.mLocale);
             try {
                 parseKeyboardSet(mResources, R.xml.keyboard_set);
             } catch (Exception e) {
@@ -112,19 +195,20 @@
             } finally {
                 LocaleUtils.setSystemLocale(mResources, savedLocale);
             }
-            return new KeyboardSet(this);
+            return new KeyboardSet(mContext, mParams);
         }
 
-        KeyboardId getKeyboardId(boolean isSymbols, boolean isShift) {
-            final int elementState = getElementState(mMode, isSymbols, isShift);
-            final int xmlId = mElementKeyboards.get(elementState);
-            final boolean hasShortcutKey = mVoiceKeyEnabled && (isSymbols != mVoiceKeyOnMain);
-            return new KeyboardId(xmlId, elementState, mLocale, mConf.orientation,
-                    mMetrics.widthPixels, mMode, mEditorInfo, mSettingsKeyEnabled, mNoSettingsKey,
-                    mVoiceKeyEnabled, hasShortcutKey);
+        // TODO: Move this method to KeyboardSet
+        static KeyboardId getKeyboardId(int elementState, boolean isSymbols, Params params) {
+            final boolean hasShortcutKey = params.mVoiceKeyEnabled
+                    && (isSymbols != params.mVoiceKeyOnMain);
+            return new KeyboardId(elementState, params.mLocale, params.mOrientation, params.mWidth,
+                    params.mMode, params.mInputType, params.mImeOptions, params.mSettingsKeyEnabled,
+                    params.mNoSettingsKey, params.mVoiceKeyEnabled, hasShortcutKey);
         }
 
-        private static int getElementState(int mode, boolean isSymbols, boolean isShift) {
+        // TODO: Move this method to KeyboardSet
+        static int getElementState(int mode, boolean isSymbols, boolean isShift) {
             switch (mode) {
             case KeyboardId.MODE_PHONE:
                 return (isSymbols && isShift)
@@ -198,7 +282,7 @@
                         R.styleable.KeyboardSet_Element_elementName, 0);
                 final int elementKeyboard = a.getResourceId(
                         R.styleable.KeyboardSet_Element_elementKeyboard, 0);
-                mElementKeyboards.put(elementName, elementKeyboard);
+                mParams.mElementKeyboards.put(elementName, elementKeyboard);
             } finally {
                 a.recycle();
             }
@@ -208,7 +292,8 @@
     public static String parseKeyboardLocale(Resources res, int resId)
             throws XmlPullParserException, IOException {
         final XmlPullParser parser = res.getXml(resId);
-        if (parser == null) return "";
+        if (parser == null)
+            return "";
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
             if (event == XmlPullParser.START_TAG) {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 3d4a6ef..4f6d5f8 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -31,21 +31,15 @@
 import com.android.inputmethod.latin.InputView;
 import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.LocaleUtils;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.Settings;
 import com.android.inputmethod.latin.SettingsValues;
 import com.android.inputmethod.latin.SubtypeSwitcher;
 import com.android.inputmethod.latin.Utils;
 
-import java.lang.ref.SoftReference;
-import java.util.HashMap;
-import java.util.Locale;
-
 public class KeyboardSwitcher implements KeyboardState.SwitchActions,
         SharedPreferences.OnSharedPreferenceChangeListener {
     private static final String TAG = KeyboardSwitcher.class.getSimpleName();
-    private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG;
 
     public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916";
     private static final int[] KEYBOARD_THEMES = {
@@ -69,9 +63,6 @@
 
     private KeyboardSet mKeyboardSet;
 
-    private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache =
-            new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
-
     /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
      * what user actually typed. */
     private boolean mIsAutoCorrectionActive;
@@ -121,25 +112,27 @@
         if (mThemeIndex != themeIndex) {
             mThemeIndex = themeIndex;
             mThemeContext = new ContextThemeWrapper(context, KEYBOARD_THEMES[themeIndex]);
-            mKeyboardCache.clear();
+            KeyboardSet.clearKeyboardCache();
         }
     }
 
     public void loadKeyboard(EditorInfo editorInfo, SettingsValues settingsValues) {
+        mKeyboardSet = new KeyboardSet.Builder(mThemeContext, editorInfo, settingsValues)
+                .build();
+        final KeyboardId mainKeyboardId = mKeyboardSet.getMainKeyboardId();
         try {
-            mKeyboardSet = new KeyboardSet.Builder(mInputMethodService, editorInfo, settingsValues)
-                    .build();
             mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols),
                     hasDistinctMultitouch());
-            // TODO: Should get rid of this special case handling for Phone Number layouts once we
-            // have separate layouts with unique KeyboardIds for alphabet and alphabet-shifted
-            // respectively.
-            if (mKeyboardSet.mAlphabetId.isPhoneKeyboard()) {
-                mState.onToggleAlphabetAndSymbols();
-            }
         } catch (RuntimeException e) {
-            Log.w(TAG, "loading keyboard failed: " + mKeyboardSet.mAlphabetId, e);
-            LatinImeLogger.logOnException(mKeyboardSet.mAlphabetId.toString(), e);
+            Log.w(TAG, "loading keyboard failed: " + mainKeyboardId, e);
+            LatinImeLogger.logOnException(mainKeyboardId.toString(), e);
+            return;
+        }
+        // TODO: Should get rid of this special case handling for Phone Number layouts once we
+        // have separate layouts with unique KeyboardIds for alphabet and alphabet-shifted
+        // respectively.
+        if (mainKeyboardId.isPhoneKeyboard()) {
+            mState.onToggleAlphabetAndSymbols();
         }
     }
 
@@ -181,39 +174,6 @@
         mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged);
     }
 
-    // TODO: Move this method to KeyboardSet.
-    private LatinKeyboard getKeyboard(Context context, KeyboardId id) {
-        final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id);
-        LatinKeyboard keyboard = (ref == null) ? null : ref.get();
-        if (keyboard == null) {
-            final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, id.mLocale);
-            try {
-                final LatinKeyboard.Builder builder = new LatinKeyboard.Builder(context);
-                builder.load(id);
-                builder.setTouchPositionCorrectionEnabled(
-                        mSubtypeSwitcher.currentSubtypeContainsExtraValueKey(
-                                LatinIME.SUBTYPE_EXTRA_VALUE_SUPPORT_TOUCH_POSITION_CORRECTION));
-                keyboard = builder.build();
-            } finally {
-                LocaleUtils.setSystemLocale(mResources, savedLocale);
-            }
-            mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard));
-
-            if (DEBUG_CACHE) {
-                Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": "
-                        + ((ref == null) ? "LOAD" : "GCed") + " id=" + id
-                        + " theme=" + themeName(keyboard.mThemeId));
-            }
-        } else if (DEBUG_CACHE) {
-            Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT  id=" + id
-                    + " theme=" + themeName(keyboard.mThemeId));
-        }
-
-        keyboard.setShiftLocked(false);
-        keyboard.setShifted(false);
-        return keyboard;
-    }
-
     public boolean isAlphabetMode() {
         final Keyboard keyboard = getLatinKeyboard();
         return keyboard != null && keyboard.mId.isAlphabetKeyboard();
@@ -343,25 +303,19 @@
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
     public void setSymbolsKeyboard() {
-        setKeyboard(getKeyboard(mThemeContext, mKeyboardSet.mSymbolsId));
+        setKeyboard(mKeyboardSet.getMainKeyboard());
     }
 
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
     public void setAlphabetKeyboard() {
-        setKeyboard(getKeyboard(mThemeContext, mKeyboardSet.mAlphabetId));
+        setKeyboard(mKeyboardSet.getSymbolsKeyboard());
     }
 
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
     public void setSymbolsShiftedKeyboard() {
-        final Keyboard keyboard = getKeyboard(mThemeContext, mKeyboardSet.mSymbolsShiftedId);
-        setKeyboard(keyboard);
-        // TODO: Remove this logic once we introduce initial keyboard shift state attribute.
-        // Symbol shift keyboard may have a shift key that has a caps lock style indicator (a.k.a.
-        // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked()
-        // that takes care of the current keyboard having such shift key or not.
-        keyboard.setShiftLocked(keyboard.hasShiftLockKey());
+        setKeyboard(mKeyboardSet.getSymbolsShiftedKeyboard());
     }
 
     public boolean isInMomentarySwitchState() {
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index abb96f0..7437e99 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -109,6 +109,8 @@
         public Key mSpaceKey = null;
         public Key mShortcutKey = null;
 
+        LatinKeyboardParams() {}
+
         @Override
         public void onAddKey(Key key) {
             super.onAddKey(key);
@@ -130,8 +132,8 @@
         }
 
         @Override
-        public Builder load(KeyboardId id) {
-            super.load(id);
+        public Builder load(int xmlId, KeyboardId id) {
+            super.load(xmlId, id);
             return this;
         }
 
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index 49b8ce7..c09541f 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -21,6 +21,7 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.os.Message;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.GestureDetector;
@@ -38,9 +39,11 @@
 import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
 import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
 import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
 import com.android.inputmethod.latin.Utils;
+import com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
 
 import java.util.WeakHashMap;
 
@@ -62,6 +65,9 @@
     // Timing constants
     private final int mKeyRepeatInterval;
 
+    // TODO: Kill process when the usability study mode was changed.
+    private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
+
     // Mini keyboard
     private PopupWindow mMoreKeysWindow;
     private MoreKeysPanel mMoreKeysPanel;
@@ -513,6 +519,30 @@
             x = (int)me.getX(index);
             y = (int)me.getY(index);
         }
+        if (ENABLE_USABILITY_STUDY_LOG) {
+            final String eventTag;
+            switch (action) {
+                case MotionEvent.ACTION_UP:
+                    eventTag = "[Up]";
+                    break;
+                case MotionEvent.ACTION_DOWN:
+                    eventTag = "[Down]";
+                    break;
+                case MotionEvent.ACTION_POINTER_UP:
+                    eventTag = "[PointerUp]";
+                    break;
+                case MotionEvent.ACTION_POINTER_DOWN:
+                    eventTag = "[PointerDown]";
+                    break;
+                default:
+                    eventTag = "[Action" + action + "]";
+                    break;
+            }
+            if (!TextUtils.isEmpty(eventTag)) {
+                UsabilityStudyLogUtils.getInstance().write(
+                        eventTag + eventTime + "," + id + "," + x + "," + y + "\t\t");
+            }
+        }
 
         if (mKeyTimerHandler.isInKeyRepeat()) {
             final PointerTracker tracker = getPointerTracker(id);
@@ -569,6 +599,10 @@
                     py = (int)me.getY(i);
                 }
                 tracker.onMoveEvent(px, py, eventTime);
+                if (ENABLE_USABILITY_STUDY_LOG) {
+                    UsabilityStudyLogUtils.getInstance().write("[Move]"  + eventTime + ","
+                            + me.getPointerId(i) + "," + px + "," + py + "\t\t");
+                }
             }
         } else {
             getPointerTracker(id).processMotionEvent(action, x, y, eventTime, this);
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
index e0f21a2..6781459 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
@@ -207,7 +207,7 @@
 
         public Builder(KeyboardView view, int xmlId, Key parentKey, Keyboard parentKeyboard) {
             super(view.getContext(), new MiniKeyboardParams());
-            load(parentKeyboard.mId.cloneWithNewXml(xmlId));
+            load(xmlId, parentKeyboard.mId);
 
             // TODO: Mini keyboard's vertical gap is currently calculated heuristically.
             // Should revise the algorithm.
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index 7382cfa..7ef471c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -267,9 +267,9 @@
         params.mTouchPositionCorrection.load(data);
     }
 
-    public KeyboardBuilder<KP> load(KeyboardId id) {
+    public KeyboardBuilder<KP> load(int xmlId, KeyboardId id) {
         mParams.mId = id;
-        final XmlResourceParser parser = mResources.getXml(id.getXmlId());
+        final XmlResourceParser parser = mResources.getXml(xmlId);
         try {
             parseKeyboard(parser);
         } catch (XmlPullParserException e) {
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
new file mode 100644
index 0000000..c5b9703
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -0,0 +1,103 @@
+/*
+ * 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.text.InputType;
+import android.util.Log;
+import android.view.inputmethod.EditorInfo;
+
+import com.android.inputmethod.compat.InputTypeCompatUtils;
+
+/**
+ * Class to hold attributes of the input field.
+ */
+public class InputAttributes {
+    private final String TAG = InputAttributes.class.getSimpleName();
+
+    final public boolean mInsertSpaceOnPickSuggestionManually;
+    final public boolean mInputTypeNoAutoCorrect;
+    final public boolean mIsSettingsSuggestionStripOn;
+    final public boolean mApplicationSpecifiedCompletionOn;
+
+    public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode) {
+        if (editorInfo == null || editorInfo.inputType == InputType.TYPE_CLASS_TEXT) {
+            mInsertSpaceOnPickSuggestionManually = false;
+            mIsSettingsSuggestionStripOn = false;
+            mInputTypeNoAutoCorrect = false;
+            mApplicationSpecifiedCompletionOn = false;
+        } else {
+            final int inputType = editorInfo.inputType;
+            if (inputType == InputType.TYPE_NULL) {
+                // TODO: We should honor TYPE_NULL specification.
+                Log.i(TAG, "InputType.TYPE_NULL is specified");
+            }
+            final int inputClass = inputType & InputType.TYPE_MASK_CLASS;
+            final int variation = inputType & InputType.TYPE_MASK_VARIATION;
+            if (inputClass == 0) {
+                // TODO: is this check still necessary?
+                Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x"
+                        + " imeOptions=0x%08x",
+                        inputType, editorInfo.imeOptions));
+            }
+            final boolean flagNoSuggestions =
+                    0 != (inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
+            final boolean flagMultiLine =
+                    0 != (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE);
+            final boolean flagAutoCorrect =
+                    0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT);
+            final boolean flagAutoComplete =
+                    0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
+
+            // Make sure that passwords are not displayed in {@link SuggestionsView}.
+            if (InputTypeCompatUtils.isPasswordInputType(inputType)
+                    || InputTypeCompatUtils.isVisiblePasswordInputType(inputType)
+                    || InputTypeCompatUtils.isEmailVariation(variation)
+                    || InputType.TYPE_TEXT_VARIATION_URI == variation
+                    || InputType.TYPE_TEXT_VARIATION_FILTER == variation
+                    || flagNoSuggestions
+                    || flagAutoComplete) {
+                mIsSettingsSuggestionStripOn = false;
+            } else {
+                mIsSettingsSuggestionStripOn = true;
+            }
+
+            if (InputTypeCompatUtils.isEmailVariation(variation)
+                    || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) {
+                // The point in turning this off is that we don't want to insert a space after
+                // a name when filling a form: we can't delete trailing spaces when changing fields
+                mInsertSpaceOnPickSuggestionManually = false;
+            } else {
+                mInsertSpaceOnPickSuggestionManually = true;
+            }
+
+            // If it's a browser edit field and auto correct is not ON explicitly, then
+            // disable auto correction, but keep suggestions on.
+            // If NO_SUGGESTIONS is set, don't do prediction.
+            // If it's not multiline and the autoCorrect flag is not set, then don't correct
+            if ((variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT
+                    && !flagAutoCorrect)
+                    || flagNoSuggestions
+                    || (!flagAutoCorrect && !flagMultiLine)) {
+                mInputTypeNoAutoCorrect = true;
+            } else {
+                mInputTypeNoAutoCorrect = false;
+            }
+
+            mApplicationSpecifiedCompletionOn = flagAutoComplete && isFullscreenMode;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index a844df1..69fc87b 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -176,6 +176,7 @@
     private int mSpaceState;
 
     private SettingsValues mSettingsValues;
+    private InputAttributes mInputAttributes;
 
     private View mExtractArea;
     private View mKeyPreviewBackingView;
@@ -196,13 +197,6 @@
     private UserUnigramDictionary mUserUnigramDictionary;
     private boolean mIsUserDictionaryAvailable;
 
-    // TODO: Create an inner class to group options and pseudo-options to improve readability.
-    // These variables are initialized according to the {@link EditorInfo#inputType}.
-    private boolean mInsertSpaceOnPickSuggestionManually;
-    private boolean mInputTypeNoAutoCorrect;
-    private boolean mIsSettingsSuggestionStripOn;
-    private boolean mApplicationSpecifiedCompletionOn;
-
     private WordComposer mWordComposer = new WordComposer();
     private boolean mHasUncommittedTypedChars;
 
@@ -515,6 +509,8 @@
 
         loadSettings();
 
+        // TODO: remove the following when it's not needed by updateCorrectionMode() any more
+        mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
         Utils.GCUtils.getInstance().reset();
         boolean tryGC = true;
         for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
@@ -757,7 +753,8 @@
         // The EditorInfo might have a flag that affects fullscreen mode.
         // Note: This call should be done by InputMethodService?
         updateFullscreenMode();
-        initializeInputAttributes(editorInfo);
+        mInputAttributes = new InputAttributes(editorInfo, isFullscreenMode());
+        mApplicationSpecifiedCompletions = null;
 
         inputView.closing();
         mEnteredText = null;
@@ -798,73 +795,6 @@
         if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
     }
 
-    private void initializeInputAttributes(EditorInfo editorInfo) {
-        if (editorInfo == null)
-            return;
-        final int inputType = editorInfo.inputType;
-        if (inputType == InputType.TYPE_NULL) {
-            // TODO: We should honor TYPE_NULL specification.
-            Log.i(TAG, "InputType.TYPE_NULL is specified");
-        }
-        final int inputClass = inputType & InputType.TYPE_MASK_CLASS;
-        final int variation = inputType & InputType.TYPE_MASK_VARIATION;
-        if (inputClass == 0) {
-            Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x imeOptions=0x%08x",
-                    inputType, editorInfo.imeOptions));
-        }
-
-        mInsertSpaceOnPickSuggestionManually = false;
-        mInputTypeNoAutoCorrect = false;
-        mIsSettingsSuggestionStripOn = false;
-        mApplicationSpecifiedCompletionOn = false;
-        mApplicationSpecifiedCompletions = null;
-
-        if (inputClass == InputType.TYPE_CLASS_TEXT) {
-            mIsSettingsSuggestionStripOn = true;
-            // Make sure that passwords are not displayed in {@link SuggestionsView}.
-            if (InputTypeCompatUtils.isPasswordInputType(inputType)
-                    || InputTypeCompatUtils.isVisiblePasswordInputType(inputType)) {
-                mIsSettingsSuggestionStripOn = false;
-            }
-            if (InputTypeCompatUtils.isEmailVariation(variation)
-                    || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) {
-                // The point in turning this off is that we don't want to insert a space after
-                // a name when filling a form: we can't delete trailing spaces when changing fields
-                mInsertSpaceOnPickSuggestionManually = false;
-            } else {
-                mInsertSpaceOnPickSuggestionManually = true;
-            }
-            if (InputTypeCompatUtils.isEmailVariation(variation)) {
-                mIsSettingsSuggestionStripOn = false;
-            } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) {
-                mIsSettingsSuggestionStripOn = false;
-            } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
-                mIsSettingsSuggestionStripOn = false;
-            } else if (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) {
-                // If it's a browser edit field and auto correct is not ON explicitly, then
-                // disable auto correction, but keep suggestions on.
-                if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) {
-                    mInputTypeNoAutoCorrect = true;
-                }
-            }
-
-            // If NO_SUGGESTIONS is set, don't do prediction.
-            if ((inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) {
-                mIsSettingsSuggestionStripOn = false;
-                mInputTypeNoAutoCorrect = true;
-            }
-            // If it's not multiline and the autoCorrect flag is not set, then don't correct
-            if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0
-                    && (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0) {
-                mInputTypeNoAutoCorrect = true;
-            }
-            if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
-                mIsSettingsSuggestionStripOn = false;
-                mApplicationSpecifiedCompletionOn = isFullscreenMode();
-            }
-        }
-    }
-
     @Override
     public void onWindowHidden() {
         super.onWindowHidden();
@@ -1016,7 +946,7 @@
                 }
             }
         }
-        if (mApplicationSpecifiedCompletionOn) {
+        if (mInputAttributes.mApplicationSpecifiedCompletionOn) {
             mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
             if (applicationSpecifiedCompletions == null) {
                 clearSuggestions();
@@ -1612,7 +1542,7 @@
             // in Italian dov' should not be expanded to dove' because the elision
             // requires the last vowel to be removed.
             final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
-                    && !mInputTypeNoAutoCorrect;
+                    && !mInputAttributes.mInputTypeNoAutoCorrect;
             if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
                 commitCurrentAutoCorrection(primaryCode, ic);
             } else {
@@ -1689,7 +1619,7 @@
     }
 
     public boolean isSuggestionsRequested() {
-        return mIsSettingsSuggestionStripOn
+        return mInputAttributes.mIsSettingsSuggestionStripOn
                 && (mCorrectionMode > 0 || isShowingSuggestionsStrip());
     }
 
@@ -1711,7 +1641,7 @@
             return true;
         if (!isShowingSuggestionsStrip())
             return false;
-        if (mApplicationSpecifiedCompletionOn)
+        if (mInputAttributes.mApplicationSpecifiedCompletionOn)
             return true;
         return isSuggestionsRequested();
     }
@@ -1798,7 +1728,8 @@
         final SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(mWordComposer,
                 prevWord, mKeyboardSwitcher.getLatinKeyboard().getProximityInfo(), mCorrectionMode);
 
-        boolean autoCorrectionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection();
+        boolean autoCorrectionAvailable = !mInputAttributes.mInputTypeNoAutoCorrect
+                && mSuggest.hasAutoCorrection();
         final CharSequence typedWord = mWordComposer.getTypedWord();
         // Here, we want to promote a whitelisted word if exists.
         // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
@@ -1914,7 +1845,8 @@
         if (ic != null) {
             ic.beginBatchEdit();
         }
-        if (mApplicationSpecifiedCompletionOn && mApplicationSpecifiedCompletions != null
+        if (mInputAttributes.mApplicationSpecifiedCompletionOn
+                && mApplicationSpecifiedCompletions != null
                 && index >= 0 && index < mApplicationSpecifiedCompletions.length) {
             if (ic != null) {
                 final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
@@ -1968,7 +1900,7 @@
         LatinImeLogger.logOnManualSuggestion(mWordComposer.getTypedWord().toString(),
                 suggestion.toString(), index, suggestions.mWords);
         // Follow it with a space
-        if (mInsertSpaceOnPickSuggestionManually) {
+        if (mInputAttributes.mInsertSpaceOnPickSuggestionManually) {
             sendMagicSpace();
         }
 
@@ -2419,7 +2351,7 @@
     private void updateCorrectionMode() {
         // TODO: cleanup messy flags
         final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
-                && !mInputTypeNoAutoCorrect;
+                && !mInputAttributes.mInputTypeNoAutoCorrect;
         mCorrectionMode = shouldAutoCorrect ? Suggest.CORRECTION_FULL : Suggest.CORRECTION_NONE;
         mCorrectionMode = (mSettingsValues.mBigramSuggestionEnabled && shouldAutoCorrect)
                 ? Suggest.CORRECTION_FULL_BIGRAM : mCorrectionMode;
@@ -2518,12 +2450,14 @@
         final Keyboard keyboard = mKeyboardSwitcher.getLatinKeyboard();
         final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1;
         p.println("  Keyboard mode = " + keyboardMode);
-        p.println("  mIsSuggestionsRequested=" + mIsSettingsSuggestionStripOn);
+        p.println("  mIsSuggestionsRequested=" + mInputAttributes.mIsSettingsSuggestionStripOn);
         p.println("  mCorrectionMode=" + mCorrectionMode);
         p.println("  mHasUncommittedTypedChars=" + mHasUncommittedTypedChars);
         p.println("  mAutoCorrectEnabled=" + mSettingsValues.mAutoCorrectEnabled);
-        p.println("  mInsertSpaceOnPickSuggestionManually=" + mInsertSpaceOnPickSuggestionManually);
-        p.println("  mApplicationSpecifiedCompletionOn=" + mApplicationSpecifiedCompletionOn);
+        p.println("  mInsertSpaceOnPickSuggestionManually="
+                + mInputAttributes.mInsertSpaceOnPickSuggestionManually);
+        p.println("  mApplicationSpecifiedCompletionOn="
+                + mInputAttributes.mApplicationSpecifiedCompletionOn);
         p.println("  mSoundOn=" + mSettingsValues.mSoundOn);
         p.println("  mVibrateOn=" + mSettingsValues.mVibrateOn);
         p.println("  mKeyPreviewPopupOn=" + mSettingsValues.mKeyPreviewPopupOn);
diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
index da5058d..6f1adfe 100644
--- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java
+++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
@@ -29,6 +29,7 @@
 
     public static boolean sDBG = false;
     public static boolean sVISUALDEBUG = false;
+    public static boolean sUsabilityStudy = false;
 
     @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index 7f59189..eeabb30 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -181,7 +181,7 @@
                 int minWidth, int maxRow) {
             final Keyboard keyboard = KeyboardSwitcher.getInstance().getLatinKeyboard();
             final int xmlId = R.xml.kbd_suggestions_pane_template;
-            load(keyboard.mId.cloneWithNewXml(xmlId));
+            load(xmlId, keyboard.mId);
             mParams.mVerticalGap = mParams.mTopPadding = keyboard.mVerticalGap / 2;
 
             final int count = mParams.layout(suggestions, fromPos, maxWidth, minWidth, maxRow,
diff --git a/native/src/proximity_info.cpp b/native/src/proximity_info.cpp
index 6857caf..6e26da2 100644
--- a/native/src/proximity_info.cpp
+++ b/native/src/proximity_info.cpp
@@ -100,9 +100,17 @@
 }
 
 bool ProximityInfo::hasSpaceProximity(const int x, const int y) const {
+    if (x < 0 || y < 0) {
+        if (DEBUG_DICT) {
+            LOGI("HasSpaceProximity: Illegal coordinates (%d, %d)", x, y);
+            assert(true);
+        }
+        return false;
+    }
+
     const int startIndex = getStartIndexFromCoordinates(x, y);
     if (DEBUG_PROXIMITY_INFO) {
-        LOGI("hasSpaceProximity: index %d", startIndex);
+        LOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y);
     }
     for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
         if (DEBUG_PROXIMITY_INFO) {
diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp
index 0db33e7..15fe971 100644
--- a/native/src/unigram_dictionary.cpp
+++ b/native/src/unigram_dictionary.cpp
@@ -182,21 +182,16 @@
         const int *xcoordinates, const int *ycoordinates, const int *codes,
         const int inputLength, const int flags, Correction *correction,
         WordsPriorityQueuePool *queuePool) {
-    WordsPriorityQueue *masterQueue = queuePool->getMasterQueue();
 
     PROF_OPEN;
     PROF_START(0);
-    initSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, inputLength, masterQueue);
-    if (DEBUG_DICT) assert(codesSize == inputLength);
-
-    const int maxDepth = min(inputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH);
-    correction->initCorrection(proximityInfo, inputLength, maxDepth);
+    // Note: This line is intentionally left blank
     PROF_END(0);
 
-    const bool useFullEditDistance = USE_FULL_EDIT_DISTANCE & flags;
-    // TODO: remove
     PROF_START(1);
-    getSuggestionCandidates(useFullEditDistance, inputLength, correction, masterQueue);
+    const bool useFullEditDistance = USE_FULL_EDIT_DISTANCE & flags;
+    getOneWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, useFullEditDistance,
+            inputLength, correction, queuePool);
     PROF_END(1);
 
     PROF_START(2);
@@ -219,8 +214,8 @@
             if (DEBUG_DICT) {
                 LOGI("--- Suggest missing space characters %d", i);
             }
-            getMissingSpaceWords(
-                    inputLength, i, proximityInfo, correction, useFullEditDistance, queuePool);
+            getMissingSpaceWords(proximityInfo, xcoordinates, ycoordinates, codes,
+                    useFullEditDistance, inputLength, i, correction, queuePool);
         }
     }
     PROF_END(5);
@@ -239,8 +234,8 @@
                         i, x, y, proximityInfo->hasSpaceProximity(x, y));
             }
             if (proximityInfo->hasSpaceProximity(x, y)) {
-                getMistypedSpaceWords(inputLength, i, proximityInfo, correction,
-                        useFullEditDistance, queuePool);
+                getMistypedSpaceWords(proximityInfo, xcoordinates, ycoordinates, codes,
+                        useFullEditDistance, inputLength, i, correction, queuePool);
             }
         }
     }
@@ -260,6 +255,18 @@
 static const char QUOTE = '\'';
 static const char SPACE = ' ';
 
+void UnigramDictionary::getOneWordSuggestions(ProximityInfo *proximityInfo,
+        const int *xcoordinates, const int *ycoordinates, const int *codes,
+        const bool useFullEditDistance, const int inputLength, Correction *correction,
+        WordsPriorityQueuePool *queuePool) {
+    WordsPriorityQueue *masterQueue = queuePool->getMasterQueue();
+    initSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, inputLength, masterQueue);
+    if (DEBUG_DICT) assert(codesSize == inputLength);
+    const int maxDepth = min(inputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH);
+    correction->initCorrection(proximityInfo, inputLength, maxDepth);
+    getSuggestionCandidates(useFullEditDistance, inputLength, correction, masterQueue);
+}
+
 void UnigramDictionary::getSuggestionCandidates(const bool useFullEditDistance,
         const int inputLength, Correction *correction, WordsPriorityQueue *queue) {
     // TODO: Remove setCorrectionParams
@@ -295,22 +302,28 @@
     }
 }
 
-void UnigramDictionary::getMissingSpaceWords(
-        const int inputLength, const int missingSpacePos, ProximityInfo *proximityInfo,
-        Correction *correction, const bool useFullEditDistance, WordsPriorityQueuePool *queuePool) {
+void UnigramDictionary::getMissingSpaceWords(ProximityInfo *proximityInfo, const int *xcoordinates,
+        const int *ycoordinates, const int *codes, const bool useFullEditDistance,
+        const int inputLength, const int missingSpacePos, Correction *correction,
+        WordsPriorityQueuePool* queuePool) {
     correction->setCorrectionParams(-1 /* skipPos */, -1 /* excessivePos */,
             -1 /* transposedPos */, -1 /* spaceProximityPos */, missingSpacePos,
             useFullEditDistance, false /* doAutoCompletion */, MAX_ERRORS_FOR_TWO_WORDS);
-    getSplitTwoWordsSuggestion(inputLength, proximityInfo, correction, queuePool);
+    getSplitTwoWordsSuggestions(proximityInfo, xcoordinates, ycoordinates, codes,
+            useFullEditDistance, inputLength, missingSpacePos, -1/* spaceProximityPos */,
+            correction, queuePool);
 }
 
-void UnigramDictionary::getMistypedSpaceWords(
-        const int inputLength, const int spaceProximityPos, ProximityInfo *proximityInfo,
-        Correction *correction, const bool useFullEditDistance, WordsPriorityQueuePool *queuePool) {
+void UnigramDictionary::getMistypedSpaceWords(ProximityInfo *proximityInfo, const int *xcoordinates,
+        const int *ycoordinates, const int *codes, const bool useFullEditDistance,
+        const int inputLength, const int spaceProximityPos, Correction *correction,
+        WordsPriorityQueuePool* queuePool) {
     correction->setCorrectionParams(-1 /* skipPos */, -1 /* excessivePos */,
             -1 /* transposedPos */, spaceProximityPos, -1 /* missingSpacePos */,
             useFullEditDistance, false /* doAutoCompletion */, MAX_ERRORS_FOR_TWO_WORDS);
-    getSplitTwoWordsSuggestion(inputLength, proximityInfo, correction, queuePool);
+    getSplitTwoWordsSuggestions(proximityInfo, xcoordinates, ycoordinates, codes,
+            useFullEditDistance, inputLength, -1 /* missingSpacePos */, spaceProximityPos,
+            correction, queuePool);
 }
 
 inline void UnigramDictionary::onTerminal(
@@ -323,13 +336,12 @@
     }
 }
 
-void UnigramDictionary::getSplitTwoWordsSuggestion(
-        const int inputLength, ProximityInfo *proximityInfo, Correction *correction,
-        WordsPriorityQueuePool *queuePool) {
+void UnigramDictionary::getSplitTwoWordsSuggestions(ProximityInfo *proximityInfo,
+        const int *xcoordinates, const int *ycoordinates, const int *codes,
+        const bool useFullEditDistance, const int inputLength, const int missingSpacePos,
+        const int  spaceProximityPos, Correction *correction, WordsPriorityQueuePool* queuePool) {
     WordsPriorityQueue *masterQueue = queuePool->getMasterQueue();
 
-    const int spaceProximityPos = correction->getSpaceProximityPos();
-    const int missingSpacePos = correction->getMissingSpacePos();
     if (DEBUG_DICT) {
         int inputCount = 0;
         if (spaceProximityPos >= 0) ++inputCount;
diff --git a/native/src/unigram_dictionary.h b/native/src/unigram_dictionary.h
index ce15cdd..27ebef1 100644
--- a/native/src/unigram_dictionary.h
+++ b/native/src/unigram_dictionary.h
@@ -91,17 +91,24 @@
     void initSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
             const int *ycoordinates, const int *codes, const int codesSize,
             WordsPriorityQueue *queue);
+    void getOneWordSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
+            const int *ycoordinates, const int *codes, const bool useFullEditDistance,
+            const int inputLength, Correction *correction, WordsPriorityQueuePool* queuePool);
     void getSuggestionCandidates(
             const bool useFullEditDistance, const int inputLength, Correction *correction,
             WordsPriorityQueue* queue);
-    void getSplitTwoWordsSuggestion(const int inputLength, ProximityInfo *proximityInfo,
-            Correction *correction, WordsPriorityQueuePool *queuePool);
-    void getMissingSpaceWords(const int inputLength, const int missingSpacePos,
-            ProximityInfo *proximityInfo, Correction *correction,
-            const bool useFullEditDistance, WordsPriorityQueuePool *queuePool);
-    void getMistypedSpaceWords(const int inputLength, const int spaceProximityPos,
-            ProximityInfo *proximityInfo, Correction *correction,
-            const bool useFullEditDistance, WordsPriorityQueuePool *queuePool);
+    void getSplitTwoWordsSuggestions(ProximityInfo *proximityInfo,
+            const int *xcoordinates, const int *ycoordinates, const int *codes,
+            const bool useFullEditDistance, const int inputLength, const int spaceProximityPos,
+            const int missingSpacePos, Correction *correction, WordsPriorityQueuePool* queuePool);
+    void getMissingSpaceWords(ProximityInfo *proximityInfo, const int *xcoordinates,
+            const int *ycoordinates, const int *codes, const bool useFullEditDistance,
+            const int inputLength, const int missingSpacePos, Correction *correction,
+            WordsPriorityQueuePool* queuePool);
+    void getMistypedSpaceWords(ProximityInfo *proximityInfo, const int *xcoordinates,
+            const int *ycoordinates, const int *codes, const bool useFullEditDistance,
+            const int inputLength, const int spaceProximityPos, Correction *correction,
+            WordsPriorityQueuePool* queuePool);
     void onTerminal(const int freq, Correction *correction, WordsPriorityQueue *queue);
     bool needsToSkipCurrentNode(const unsigned short c,
             const int inputIndex, const int skipPos, const int depth);
diff --git a/native/src/words_priority_queue.h b/native/src/words_priority_queue.h
index a4175d3..2d62709 100644
--- a/native/src/words_priority_queue.h
+++ b/native/src/words_priority_queue.h
@@ -111,6 +111,10 @@
         return size;
     }
 
+    int size() {
+        return mSuggestions.size();
+    }
+
     void clear() {
         while (!mSuggestions.empty()) {
             SuggestedWord* sw = mSuggestions.top();
diff --git a/tests/src/com/android/inputmethod/latin/SuggestHelper.java b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
index 06b1924..4d123e5 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestHelper.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
@@ -32,11 +32,13 @@
     protected final LatinKeyboard mKeyboard;
     private final KeyDetector mKeyDetector;
 
+    public static final int ALPHABET_KEYBOARD = com.android.inputmethod.latin.R.xml.kbd_qwerty;
+
     public SuggestHelper(Context context, int dictionaryId, KeyboardId keyboardId) {
         // Use null as the locale for Suggest so as to force it to use the internal dictionary
         // (and not try to find a dictionary provider for a specified locale)
         mSuggest = new Suggest(context, dictionaryId, null);
-        mKeyboard = new LatinKeyboard.Builder(context).load(keyboardId).build();
+        mKeyboard = new LatinKeyboard.Builder(context).load(ALPHABET_KEYBOARD, keyboardId).build();
         mKeyDetector = new KeyDetector(0);
         init();
     }
@@ -45,7 +47,7 @@
             final long startOffset, final long length, final KeyboardId keyboardId,
             final Locale locale) {
         mSuggest = new Suggest(context, dictionaryPath, startOffset, length, null, locale);
-        mKeyboard = new LatinKeyboard.Builder(context).load(keyboardId).build();
+        mKeyboard = new LatinKeyboard.Builder(context).load(ALPHABET_KEYBOARD, keyboardId).build();
         mKeyDetector = new KeyDetector(0);
         init();
     }
diff --git a/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java b/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java
index 7b4c6a9..9dd61d7 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java
@@ -19,6 +19,7 @@
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Configuration;
 import android.test.AndroidTestCase;
+import android.text.InputType;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 
@@ -49,9 +50,8 @@
                     + "orientation=" + orientation);
             return null;
         }
-        return new KeyboardId(com.android.inputmethod.latin.R.xml.kbd_qwerty,
-                KeyboardId.ELEMENT_ALPHABET, locale, orientation, width, KeyboardId.MODE_TEXT,
-                null, false, false, false, false);
+        return new KeyboardId(KeyboardId.ELEMENT_ALPHABET, locale, orientation, width,
+                KeyboardId.MODE_TEXT, InputType.TYPE_CLASS_TEXT, 0, false, false, false, false);
     }
 
     protected InputStream openTestRawResource(int resIdInTest) {