Merge "Cleanup FrameLayoutCompatUtils to ViewLayoutUtils"
diff --git a/java/proguard.flags b/java/proguard.flags
index a0010f9..2ee1a26 100644
--- a/java/proguard.flags
+++ b/java/proguard.flags
@@ -40,6 +40,10 @@
   void setLogFileManager(...);
 }
 
+-keep class com.android.inputmethod.keyboard.KeyboardSet$Builder {
+  void setTouchPositionCorrectionEnabled(...);
+}
+
 # The support library contains references to newer platform versions.
 # Don't warn about those in case this app is linking against an older
 # platform version.  We know about them, and they are safe.
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index ebca250..c5c647a 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -21,9 +21,6 @@
     <PreferenceCategory
         android:title="@string/general_category"
         android:key="general_settings">
-        <PreferenceScreen
-            android:key="subtype_settings"
-            android:title="@string/language_selection_title" />
         <CheckBoxPreference
             android:key="auto_cap"
             android:title="@string/auto_cap"
diff --git a/java/src/com/android/inputmethod/compat/AbstractCompatWrapper.java b/java/src/com/android/inputmethod/compat/AbstractCompatWrapper.java
deleted file mode 100644
index 2c31c55..0000000
--- a/java/src/com/android/inputmethod/compat/AbstractCompatWrapper.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.compat;
-
-import android.util.Log;
-
-public abstract class AbstractCompatWrapper {
-    private static final String TAG = AbstractCompatWrapper.class.getSimpleName();
-    protected final Object mObj;
-
-    public AbstractCompatWrapper(Object obj) {
-        if (obj == null) {
-            Log.e(TAG, "Invalid input to AbstractCompatWrapper");
-        }
-        mObj = obj;
-    }
-
-    public Object getOriginalObject() {
-        return mObj;
-    }
-
-    public boolean hasOriginalObject() {
-        return mObj != null;
-    }
-}
diff --git a/java/src/com/android/inputmethod/compat/CompatUtils.java b/java/src/com/android/inputmethod/compat/CompatUtils.java
index ba82a06..ce427e9 100644
--- a/java/src/com/android/inputmethod/compat/CompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/CompatUtils.java
@@ -23,8 +23,6 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
 
 public class CompatUtils {
     private static final String TAG = CompatUtils.class.getSimpleName();
@@ -35,23 +33,16 @@
 
     public static Intent getInputLanguageSelectionIntent(String inputMethodId,
             int flagsForSubtypeSettings) {
-        final String action;
-        Intent intent;
-        if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED
-                /* android.os.Build.VERSION_CODES.HONEYCOMB */
-                && android.os.Build.VERSION.SDK_INT >=  11) {
-            // Refer to android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS
-            action = INPUT_METHOD_SUBTYPE_SETTINGS;
-            intent = new Intent(action);
-            if (!TextUtils.isEmpty(inputMethodId)) {
-                intent.putExtra(EXTRA_INPUT_METHOD_ID, inputMethodId);
-            }
-            if (flagsForSubtypeSettings > 0) {
-                intent.setFlags(flagsForSubtypeSettings);
-            }
-            return intent;
+        // Refer to android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS
+        final String action = INPUT_METHOD_SUBTYPE_SETTINGS;
+        final Intent intent = new Intent(action);
+        if (!TextUtils.isEmpty(inputMethodId)) {
+            intent.putExtra(EXTRA_INPUT_METHOD_ID, inputMethodId);
         }
-        throw new RuntimeException("Language selection doesn't supported on this platform");
+        if (flagsForSubtypeSettings > 0) {
+            intent.setFlags(flagsForSubtypeSettings);
+        }
+        return intent;
     }
 
     public static Class<?> getClass(String className) {
@@ -138,15 +129,4 @@
             Log.e(TAG, "Exception in setFieldValue: " + e.getClass().getSimpleName());
         }
     }
-
-    public static List<InputMethodSubtypeCompatWrapper> copyInputMethodSubtypeListToWrapper(
-            Object listObject) {
-        if (!(listObject instanceof List<?>)) return null;
-        final List<InputMethodSubtypeCompatWrapper> subtypes =
-                new ArrayList<InputMethodSubtypeCompatWrapper>();
-        for (Object o: (List<?>)listObject) {
-            subtypes.add(new InputMethodSubtypeCompatWrapper(o));
-        }
-        return subtypes;
-    }
 }
diff --git a/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java
deleted file mode 100644
index 8315598..0000000
--- a/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.compat;
-
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.view.inputmethod.InputMethodInfo;
-
-import java.lang.reflect.Method;
-
-public class InputMethodInfoCompatWrapper {
-    private final InputMethodInfo mImi;
-    private static final Method METHOD_getSubtypeAt = CompatUtils.getMethod(
-            InputMethodInfo.class, "getSubtypeAt", int.class);
-    private static final Method METHOD_getSubtypeCount = CompatUtils.getMethod(
-            InputMethodInfo.class, "getSubtypeCount");
-
-    public InputMethodInfoCompatWrapper(InputMethodInfo imi) {
-        mImi = imi;
-    }
-
-    public InputMethodInfo getInputMethodInfo() {
-        return mImi;
-    }
-
-    public String getId() {
-        return mImi.getId();
-    }
-
-    public String getPackageName() {
-        return mImi.getPackageName();
-    }
-
-    public ServiceInfo getServiceInfo() {
-        return mImi.getServiceInfo();
-    }
-
-    public int getSubtypeCount() {
-        return (Integer) CompatUtils.invoke(mImi, 0, METHOD_getSubtypeCount);
-    }
-
-    public InputMethodSubtypeCompatWrapper getSubtypeAt(int index) {
-        return new InputMethodSubtypeCompatWrapper(CompatUtils.invoke(mImi, null,
-                METHOD_getSubtypeAt, index));
-    }
-
-    public CharSequence loadLabel(PackageManager pm) {
-        return mImi.loadLabel(pm);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof InputMethodInfoCompatWrapper) {
-            return mImi.equals(((InputMethodInfoCompatWrapper)o).mImi);
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return mImi.hashCode();
-    }
-}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
index 3df6bea..7be95a0 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
@@ -16,69 +16,29 @@
 
 package com.android.inputmethod.compat;
 
-import android.app.AlertDialog;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
+import android.inputmethodservice.InputMethodService;
 import android.os.IBinder;
-import android.text.TextUtils;
 import android.util.Log;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SubtypeSwitcher;
-import com.android.inputmethod.latin.SubtypeUtils;
+import android.view.inputmethod.InputMethodSubtype;
 
 import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 
 // TODO: Override this class with the concrete implementation if we need to take care of the
 // performance.
 public class InputMethodManagerCompatWrapper {
     private static final String TAG = InputMethodManagerCompatWrapper.class.getSimpleName();
-    private static final Method METHOD_getCurrentInputMethodSubtype =
-            CompatUtils.getMethod(InputMethodManager.class, "getCurrentInputMethodSubtype");
-    private static final Method METHOD_getLastInputMethodSubtype =
-            CompatUtils.getMethod(InputMethodManager.class, "getLastInputMethodSubtype");
-    private static final Method METHOD_getEnabledInputMethodSubtypeList =
-            CompatUtils.getMethod(InputMethodManager.class, "getEnabledInputMethodSubtypeList",
-                    InputMethodInfo.class, boolean.class);
-    private static final Method METHOD_getShortcutInputMethodsAndSubtypes =
-            CompatUtils.getMethod(InputMethodManager.class, "getShortcutInputMethodsAndSubtypes");
-    private static final Method METHOD_setInputMethodAndSubtype =
-            CompatUtils.getMethod(
-                    InputMethodManager.class, "setInputMethodAndSubtype", IBinder.class,
-                    String.class, InputMethodSubtypeCompatWrapper.CLASS_InputMethodSubtype);
-    private static final Method METHOD_switchToLastInputMethod = CompatUtils.getMethod(
-            InputMethodManager.class, "switchToLastInputMethod", IBinder.class);
     private static final Method METHOD_switchToNextInputMethod = CompatUtils.getMethod(
             InputMethodManager.class, "switchToNextInputMethod", IBinder.class, Boolean.TYPE);
 
     private static final InputMethodManagerCompatWrapper sInstance =
             new InputMethodManagerCompatWrapper();
 
-    // For the compatibility, IMM will create dummy subtypes if subtypes are not found.
-    // This is required to be false if the current behavior is broken. For now, it's ok to be true.
-    public static final boolean FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES =
-            !InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED;
-    private static final String VOICE_MODE = "voice";
-    private static final String KEYBOARD_MODE = "keyboard";
-
-    private InputMethodServiceCompatWrapper mService;
     private InputMethodManager mImm;
-    private PackageManager mPackageManager;
-    private ApplicationInfo mApplicationInfo;
-    private String mLatinImePackageName;
 
     public static InputMethodManagerCompatWrapper getInstance() {
         if (sInstance.mImm == null)
@@ -86,120 +46,35 @@
         return sInstance;
     }
 
-    public static void init(InputMethodServiceCompatWrapper service) {
-        sInstance.mService = service;
+    public static void init(InputMethodService service) {
         sInstance.mImm = (InputMethodManager) service.getSystemService(
                 Context.INPUT_METHOD_SERVICE);
-        sInstance.mLatinImePackageName = service.getPackageName();
-        sInstance.mPackageManager = service.getPackageManager();
-        sInstance.mApplicationInfo = service.getApplicationInfo();
     }
 
-    public InputMethodSubtypeCompatWrapper getCurrentInputMethodSubtype() {
-        Object o = CompatUtils.invoke(mImm, null, METHOD_getCurrentInputMethodSubtype);
-        return new InputMethodSubtypeCompatWrapper(o);
+    public InputMethodSubtype getCurrentInputMethodSubtype() {
+        return mImm.getCurrentInputMethodSubtype();
     }
 
-    public InputMethodSubtypeCompatWrapper getLastInputMethodSubtype() {
-        Object o = CompatUtils.invoke(mImm, null, METHOD_getLastInputMethodSubtype);
-        return new InputMethodSubtypeCompatWrapper(o);
+    public InputMethodSubtype getLastInputMethodSubtype() {
+        return mImm.getLastInputMethodSubtype();
     }
 
-    public List<InputMethodSubtypeCompatWrapper> getEnabledInputMethodSubtypeList(
-            InputMethodInfoCompatWrapper imi, boolean allowsImplicitlySelectedSubtypes) {
-        Object retval = CompatUtils.invoke(mImm, null, METHOD_getEnabledInputMethodSubtypeList,
-                (imi != null ? imi.getInputMethodInfo() : null), allowsImplicitlySelectedSubtypes);
-        if (retval == null || !(retval instanceof List<?>) || ((List<?>)retval).isEmpty()) {
-            if (!FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES) {
-                // Returns an empty list
-                return Collections.emptyList();
-            }
-            // Creates dummy subtypes
-            @SuppressWarnings("unused")
-            List<InputMethodSubtypeCompatWrapper> subtypeList =
-                    new ArrayList<InputMethodSubtypeCompatWrapper>();
-            InputMethodSubtypeCompatWrapper keyboardSubtype = getLastResortSubtype(KEYBOARD_MODE);
-            InputMethodSubtypeCompatWrapper voiceSubtype = getLastResortSubtype(VOICE_MODE);
-            if (keyboardSubtype != null) {
-                subtypeList.add(keyboardSubtype);
-            }
-            if (voiceSubtype != null) {
-                subtypeList.add(voiceSubtype);
-            }
-            return subtypeList;
-        }
-        return CompatUtils.copyInputMethodSubtypeListToWrapper(retval);
+    public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(
+            InputMethodInfo imi, boolean allowsImplicitlySelectedSubtypes) {
+        return mImm.getEnabledInputMethodSubtypeList(imi, allowsImplicitlySelectedSubtypes);
     }
 
-    private InputMethodInfoCompatWrapper getLatinImeInputMethodInfo() {
-        if (TextUtils.isEmpty(mLatinImePackageName))
-            return null;
-        return SubtypeUtils.getInputMethodInfo(mLatinImePackageName);
-    }
-
-    private static InputMethodSubtypeCompatWrapper getLastResortSubtype(String mode) {
-        if (VOICE_MODE.equals(mode) && !FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES)
-            return null;
-        Locale inputLocale = SubtypeSwitcher.getInstance().getInputLocale();
-        if (inputLocale == null)
-            return null;
-        return new InputMethodSubtypeCompatWrapper(0, 0, inputLocale.toString(), mode, "");
-    }
-
-    public Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>>
-            getShortcutInputMethodsAndSubtypes() {
-        Object retval = CompatUtils.invoke(mImm, null, METHOD_getShortcutInputMethodsAndSubtypes);
-        if (retval == null || !(retval instanceof Map<?, ?>) || ((Map<?, ?>)retval).isEmpty()) {
-            if (!FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES) {
-                // Returns an empty map
-                return Collections.emptyMap();
-            }
-            // Creates dummy subtypes
-            @SuppressWarnings("unused")
-            InputMethodInfoCompatWrapper imi = getLatinImeInputMethodInfo();
-            InputMethodSubtypeCompatWrapper voiceSubtype = getLastResortSubtype(VOICE_MODE);
-            if (imi != null && voiceSubtype != null) {
-                Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>>
-                        shortcutMap =
-                                new HashMap<InputMethodInfoCompatWrapper,
-                                        List<InputMethodSubtypeCompatWrapper>>();
-                List<InputMethodSubtypeCompatWrapper> subtypeList =
-                        new ArrayList<InputMethodSubtypeCompatWrapper>();
-                subtypeList.add(voiceSubtype);
-                shortcutMap.put(imi, subtypeList);
-                return shortcutMap;
-            } else {
-                return Collections.emptyMap();
-            }
-        }
-        Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>> shortcutMap =
-                new HashMap<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>>();
-        final Map<?, ?> retvalMap = (Map<?, ?>)retval;
-        for (Object key : retvalMap.keySet()) {
-            if (!(key instanceof InputMethodInfo)) {
-                Log.e(TAG, "Class type error.");
-                return null;
-            }
-            shortcutMap.put(new InputMethodInfoCompatWrapper((InputMethodInfo)key),
-                    CompatUtils.copyInputMethodSubtypeListToWrapper(retvalMap.get(key)));
-        }
-        return shortcutMap;
+    public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
+        return mImm.getShortcutInputMethodsAndSubtypes();
     }
 
     // We don't call this method when we switch between subtypes within this IME.
-    public void setInputMethodAndSubtype(
-            IBinder token, String id, InputMethodSubtypeCompatWrapper subtype) {
-        // TODO: Support subtype change on non-subtype-supported platform.
-        if (subtype != null && subtype.hasOriginalObject()) {
-            CompatUtils.invoke(mImm, null, METHOD_setInputMethodAndSubtype,
-                    token, id, subtype.getOriginalObject());
-        } else {
-            mImm.setInputMethod(token, id);
-        }
+    public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
+        mImm.setInputMethodAndSubtype(token, id, subtype);
     }
 
     public boolean switchToLastInputMethod(IBinder token) {
-        return (Boolean)CompatUtils.invoke(mImm, false, METHOD_switchToLastInputMethod, token);
+        return mImm.switchToLastInputMethod(token);
     }
 
     public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
@@ -207,13 +82,9 @@
                 onlyCurrentIme);
     }
 
-    public List<InputMethodInfoCompatWrapper> getEnabledInputMethodList() {
+    public List<InputMethodInfo> getEnabledInputMethodList() {
         if (mImm == null) return null;
-        List<InputMethodInfoCompatWrapper> imis = new ArrayList<InputMethodInfoCompatWrapper>();
-        for (InputMethodInfo imi : mImm.getEnabledInputMethodList()) {
-            imis.add(new InputMethodInfoCompatWrapper(imi));
-        }
-        return imis;
+        return mImm.getEnabledInputMethodList();
     }
 
     public void showInputMethodPicker() {
diff --git a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
deleted file mode 100644
index 7c15be3..0000000
--- a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.compat;
-
-import android.app.AlertDialog;
-import android.inputmethodservice.InputMethodService;
-import android.os.IBinder;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
-import com.android.inputmethod.latin.SubtypeSwitcher;
-
-public class InputMethodServiceCompatWrapper extends InputMethodService {
-    // CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED needs to be false if the API level is 10
-    // or previous. Note that InputMethodSubtype was added in the API level 11.
-    // For the API level 11 or later, LatinIME should override onCurrentInputMethodSubtypeChanged().
-    // For the API level 10 or previous, we handle the "subtype changed" events by ourselves
-    // without having support from framework -- onCurrentInputMethodSubtypeChanged().
-    public static final boolean CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED = true;
-
-    private InputMethodManagerCompatWrapper mImm;
-
-    // For compatibility of {@link InputMethodManager#showInputMethodPicker}.
-    // TODO: Move this variable back to LatinIME when this compatibility wrapper is removed.
-    protected AlertDialog mOptionsDialog;
-
-    public void showOptionDialogInternal(AlertDialog dialog) {
-        final IBinder windowToken = KeyboardSwitcher.getInstance().getKeyboardView()
-                .getWindowToken();
-        if (windowToken == null) return;
-
-        dialog.setCancelable(true);
-        dialog.setCanceledOnTouchOutside(true);
-
-        final Window window = dialog.getWindow();
-        final WindowManager.LayoutParams lp = window.getAttributes();
-        lp.token = windowToken;
-        lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
-        window.setAttributes(lp);
-        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
-
-        mOptionsDialog = dialog;
-        dialog.show();
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mImm = InputMethodManagerCompatWrapper.getInstance();
-    }
-
-    // When the API level is 10 or previous, notifyOnCurrentInputMethodSubtypeChanged should
-    // handle the event the current subtype was changed. LatinIME calls
-    // notifyOnCurrentInputMethodSubtypeChanged every time LatinIME
-    // changes the current subtype.
-    // This call is required to let LatinIME itself know a subtype changed
-    // event when the API level is 10 or previous.
-    @SuppressWarnings("unused")
-    public void notifyOnCurrentInputMethodSubtypeChanged(
-            InputMethodSubtypeCompatWrapper newSubtype) {
-        // Do nothing when the API level is 11 or later
-        // and FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES is not true
-        if (CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED && !InputMethodManagerCompatWrapper.
-                FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES) {
-            return;
-        }
-        final InputMethodSubtypeCompatWrapper subtype = (newSubtype == null)
-                ? mImm.getCurrentInputMethodSubtype()
-                : newSubtype;
-        if (subtype != null) {
-            if (!InputMethodManagerCompatWrapper.FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES
-                    && !subtype.isDummy()) return;
-            SubtypeSwitcher.getInstance().updateSubtype(subtype);
-        }
-    }
-
-    //////////////////////////////////////
-    // Functions using API v11 or later //
-    //////////////////////////////////////
-    @Override
-    public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
-        // Do nothing when the API level is 10 or previous
-        if (!CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) return;
-        SubtypeSwitcher.getInstance().updateSubtype(
-                new InputMethodSubtypeCompatWrapper(subtype));
-    }
-
-    protected static void setTouchableRegionCompat(InputMethodService.Insets outInsets,
-            int x, int y, int width, int height) {
-        outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
-        outInsets.touchableRegion.set(x, y, width, height);
-    }
-}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
deleted file mode 100644
index 5741588..0000000
--- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * 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.compat;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.latin.LatinImeLogger;
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.Locale;
-
-// TODO: Override this class with the concrete implementation if we need to take care of the
-// performance.
-public class InputMethodSubtypeCompatWrapper extends AbstractCompatWrapper {
-    private static final boolean DBG = LatinImeLogger.sDBG;
-    private static final String TAG = InputMethodSubtypeCompatWrapper.class.getSimpleName();
-    private static final String DEFAULT_LOCALE = "en_US";
-    private static final String DEFAULT_MODE = "keyboard";
-
-    public static final Class<?> CLASS_InputMethodSubtype =
-            CompatUtils.getClass("android.view.inputmethod.InputMethodSubtype");
-    private static final Method METHOD_getNameResId =
-            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getNameResId");
-    private static final Method METHOD_getIconResId =
-            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getIconResId");
-    private static final Method METHOD_getLocale =
-            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getLocale");
-    private static final Method METHOD_getMode =
-            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getMode");
-    private static final Method METHOD_getExtraValue =
-            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getExtraValue");
-    private static final Method METHOD_containsExtraValueKey =
-            CompatUtils.getMethod(CLASS_InputMethodSubtype, "containsExtraValueKey", String.class);
-    private static final Method METHOD_getExtraValueOf =
-            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getExtraValueOf", String.class);
-    private static final Method METHOD_isAuxiliary =
-            CompatUtils.getMethod(CLASS_InputMethodSubtype, "isAuxiliary");
-    private static final Method METHOD_getDisplayName =
-            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getDisplayName", Context.class,
-                    String.class, ApplicationInfo.class);
-
-    private final int mDummyNameResId;
-    private final int mDummyIconResId;
-    private final String mDummyLocale;
-    private final String mDummyMode;
-    private final String mDummyExtraValues;
-
-    public InputMethodSubtypeCompatWrapper(Object subtype) {
-        super((CLASS_InputMethodSubtype != null && CLASS_InputMethodSubtype.isInstance(subtype))
-                ? subtype : new Object());
-        mDummyNameResId = 0;
-        mDummyIconResId = 0;
-        mDummyLocale = DEFAULT_LOCALE;
-        mDummyMode = DEFAULT_MODE;
-        mDummyExtraValues = "";
-    }
-
-    // Constructor for creating a dummy subtype.
-    public InputMethodSubtypeCompatWrapper(int nameResId, int iconResId, String locale,
-            String mode, String extraValues) {
-        super(new Object());
-        if (DBG) {
-            Log.d(TAG, "CreateInputMethodSubtypeCompatWrapper");
-        }
-        mDummyNameResId = nameResId;
-        mDummyIconResId = iconResId;
-        mDummyLocale = locale != null ? locale : "";
-        mDummyMode = mode != null ? mode : "";
-        mDummyExtraValues = extraValues != null ? extraValues : "";
-    }
-
-    public int getNameResId() {
-        if (mObj == null) return mDummyNameResId;
-        return (Integer)CompatUtils.invoke(mObj, 0, METHOD_getNameResId);
-    }
-
-    public int getIconResId() {
-        if (mObj == null) return mDummyIconResId;
-        return (Integer)CompatUtils.invoke(mObj, 0, METHOD_getIconResId);
-    }
-
-    public String getLocale() {
-        if (mObj == null) return mDummyLocale;
-        final String s = (String)CompatUtils.invoke(mObj, null, METHOD_getLocale);
-        return s != null ? s : DEFAULT_LOCALE;
-    }
-
-    public String getMode() {
-        if (mObj == null) return mDummyMode;
-        String s = (String)CompatUtils.invoke(mObj, null, METHOD_getMode);
-        if (TextUtils.isEmpty(s)) return DEFAULT_MODE;
-        return s;
-    }
-
-    public String getExtraValue() {
-        if (mObj == null) return mDummyExtraValues;
-        return (String)CompatUtils.invoke(mObj, null, METHOD_getExtraValue);
-    }
-
-    public boolean containsExtraValueKey(String key) {
-        return (Boolean)CompatUtils.invoke(mObj, false, METHOD_containsExtraValueKey, key);
-    }
-
-    public String getExtraValueOf(String key) {
-        return (String)CompatUtils.invoke(mObj, null, METHOD_getExtraValueOf, key);
-    }
-
-    public boolean isAuxiliary() {
-        return (Boolean)CompatUtils.invoke(mObj, false, METHOD_isAuxiliary);
-    }
-
-    public CharSequence getDisplayName(Context context, String packageName,
-            ApplicationInfo appInfo) {
-        if (mObj != null) {
-            return (CharSequence)CompatUtils.invoke(
-                    mObj, "", METHOD_getDisplayName, context, packageName, appInfo);
-        }
-
-        // The code below are based on {@link InputMethodSubtype#getDisplayName}.
-
-        final Locale locale = new Locale(getLocale());
-        final String localeStr = locale.getDisplayName();
-        if (getNameResId() == 0) {
-            return localeStr;
-        }
-        final CharSequence subtypeName = context.getText(getNameResId());
-        if (!TextUtils.isEmpty(localeStr)) {
-            return String.format(subtypeName.toString(), localeStr);
-        } else {
-            return localeStr;
-        }
-    }
-
-    public boolean isDummy() {
-        return !hasOriginalObject();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof InputMethodSubtypeCompatWrapper) {
-            InputMethodSubtypeCompatWrapper subtype = (InputMethodSubtypeCompatWrapper)o;
-            if (mObj == null) {
-                // easy check of dummy subtypes
-                return (mDummyNameResId == subtype.mDummyNameResId
-                        && mDummyIconResId == subtype.mDummyIconResId
-                        && mDummyLocale.equals(subtype.mDummyLocale)
-                        && mDummyMode.equals(subtype.mDummyMode)
-                        && mDummyExtraValues.equals(subtype.mDummyExtraValues));
-            }
-            return mObj.equals(subtype.getOriginalObject());
-        } else {
-            return mObj.equals(o);
-        }
-    }
-
-    @Override
-    public int hashCode() {
-        if (mObj == null) {
-            return hashCodeInternal(mDummyNameResId, mDummyIconResId, mDummyLocale,
-                    mDummyMode, mDummyExtraValues);
-        }
-        return mObj.hashCode();
-    }
-
-    private static int hashCodeInternal(int nameResId, int iconResId, String locale,
-            String mode, String extraValue) {
-        return Arrays
-                .hashCode(new Object[] { nameResId, iconResId, locale, mode, extraValue });
-    }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
index f0c773f..4d0f003 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
@@ -237,7 +237,6 @@
             return this;
         }
 
-        // TODO: Use InputMethodSubtype object as argument.
         public Builder setSubtype(Locale inputLocale, boolean asciiCapable) {
             final boolean deprecatedForceAscii = StringUtils.inPrivateImeOptions(
                     mPackageName, LatinIME.IME_OPTION_FORCE_ASCII, mEditorInfo);
diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java
index ef88f99..38444a1 100644
--- a/java/src/com/android/inputmethod/latin/AutoCorrection.java
+++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -30,8 +32,9 @@
         // Purely static class: can't instantiate.
     }
 
-    public static CharSequence computeAutoCorrectionWord(HashMap<String, Dictionary> dictionaries,
-            WordComposer wordComposer, ArrayList<CharSequence> suggestions, int[] sortedScores,
+    public static CharSequence computeAutoCorrectionWord(
+            HashMap<String, Dictionary> dictionaries,
+            WordComposer wordComposer, ArrayList<SuggestedWordInfo> suggestions,
             CharSequence consideredWord, double autoCorrectionThreshold,
             CharSequence whitelistedWord) {
         if (hasAutoCorrectionForWhitelistedWord(whitelistedWord)) {
@@ -40,8 +43,8 @@
                 dictionaries, wordComposer, suggestions, consideredWord)) {
             return consideredWord;
         } else if (hasAutoCorrectionForBinaryDictionary(wordComposer, suggestions,
-                sortedScores, consideredWord, autoCorrectionThreshold)) {
-            return suggestions.get(0);
+                consideredWord, autoCorrectionThreshold)) {
+            return suggestions.get(0).mWord;
         }
         return null;
     }
@@ -89,22 +92,23 @@
 
     private static boolean hasAutoCorrectionForConsideredWord(
             HashMap<String, Dictionary> dictionaries, WordComposer wordComposer,
-            ArrayList<CharSequence> suggestions, CharSequence consideredWord) {
+            ArrayList<SuggestedWordInfo> suggestions, CharSequence consideredWord) {
         if (TextUtils.isEmpty(consideredWord)) return false;
         return wordComposer.size() > 1 && suggestions.size() > 0
                 && !allowsToBeAutoCorrected(dictionaries, consideredWord, false);
     }
 
     private static boolean hasAutoCorrectionForBinaryDictionary(WordComposer wordComposer,
-            ArrayList<CharSequence> suggestions, int[] sortedScores,
+            ArrayList<SuggestedWordInfo> suggestions,
             CharSequence consideredWord, double autoCorrectionThreshold) {
-        if (wordComposer.size() > 1 && suggestions.size() > 0 && sortedScores.length > 0) {
-            final CharSequence autoCorrectionSuggestion = suggestions.get(0);
-            final int autoCorrectionSuggestionScore = sortedScores[0];
+        if (wordComposer.size() > 1 && suggestions.size() > 0) {
+            final SuggestedWordInfo autoCorrectionSuggestion = suggestions.get(0);
+            //final int autoCorrectionSuggestionScore = sortedScores[0];
+            final int autoCorrectionSuggestionScore = autoCorrectionSuggestion.mScore;
             // TODO: when the normalized score of the first suggestion is nearly equals to
             //       the normalized score of the second suggestion, behave less aggressive.
             final double normalizedScore = BinaryDictionary.calcNormalizedScore(
-                    consideredWord.toString(), autoCorrectionSuggestion.toString(),
+                    consideredWord.toString(), autoCorrectionSuggestion.mWord.toString(),
                     autoCorrectionSuggestionScore);
             if (DBG) {
                 Log.d(TAG, "Normalized " + consideredWord + "," + autoCorrectionSuggestion + ","
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 363494b..3b41e3b 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -45,20 +45,19 @@
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewParent;
+import android.view.Window;
+import android.view.WindowManager;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.accessibility.AccessibilityUtils;
 import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
 import com.android.inputmethod.compat.CompatUtils;
 import com.android.inputmethod.compat.EditorInfoCompatUtils;
 import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
-import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
-import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper;
-import com.android.inputmethod.compat.InputTypeCompatUtils;
 import com.android.inputmethod.compat.SuggestionSpanUtils;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardActionListener;
@@ -77,7 +76,7 @@
 /**
  * Input method implementation for Qwerty'ish keyboard.
  */
-public class LatinIME extends InputMethodServiceCompatWrapper implements KeyboardActionListener,
+public class LatinIME extends InputMethodService implements KeyboardActionListener,
         SuggestionsView.Listener {
     private static final String TAG = LatinIME.class.getSimpleName();
     private static final boolean TRACE = false;
@@ -227,6 +226,8 @@
 
     private boolean mIsAutoCorrectionIndicatorOn;
 
+    private AlertDialog mOptionsDialog;
+
     public final UIHandler mHandler = new UIHandler(this);
 
     public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
@@ -629,6 +630,11 @@
         mHandler.onFinishInput();
     }
 
+    @Override
+    public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
+        SubtypeSwitcher.getInstance().updateSubtype(subtype);
+    }
+
     private void onStartInputInternal(EditorInfo editorInfo, boolean restarting) {
         super.onStartInput(editorInfo, restarting);
     }
@@ -970,7 +976,8 @@
             final int touchHeight = inputView.getHeight() + extraHeight
                     // Extend touchable region below the keyboard.
                     + EXTENDED_TOUCHABLE_REGION_HEIGHT;
-            setTouchableRegionCompat(outInsets, 0, touchY, touchWidth, touchHeight);
+            outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
+            outInsets.touchableRegion.set(0, touchY, touchWidth, touchHeight);
         }
         outInsets.contentTopInsets = touchY;
         outInsets.visibleTopInsets = touchY;
@@ -1180,7 +1187,7 @@
         final boolean includesOtherImes = mSettingsValues.mIncludesOtherImesInLanguageSwitchList;
         final IBinder token = getWindow().getWindow().getAttributes().token;
         if (mShouldSwitchToLastSubtype) {
-            final InputMethodSubtypeCompatWrapper lastSubtype = mImm.getLastInputMethodSubtype();
+            final InputMethodSubtype lastSubtype = mImm.getLastInputMethodSubtype();
             final boolean lastSubtypeBelongsToThisIme = SubtypeUtils.checkIfSubtypeBelongsToThisIme(
                     this, lastSubtype);
             if ((includesOtherImes || lastSubtypeBelongsToThisIme)
@@ -2265,6 +2272,25 @@
         showOptionDialogInternal(builder.create());
     }
 
+    private void showOptionDialogInternal(AlertDialog dialog) {
+        final IBinder windowToken = KeyboardSwitcher.getInstance().getKeyboardView()
+                .getWindowToken();
+        if (windowToken == null) return;
+
+        dialog.setCancelable(true);
+        dialog.setCanceledOnTouchOutside(true);
+
+        final Window window = dialog.getWindow();
+        final WindowManager.LayoutParams lp = window.getAttributes();
+        lp.token = windowToken;
+        lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+        window.setAttributes(lp);
+        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+
+        mOptionsDialog = dialog;
+        dialog.show();
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
         super.dump(fd, fout, args);
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index fd61292..7b98a71 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -18,7 +18,6 @@
 
 import android.app.Activity;
 import android.app.AlertDialog;
-import android.app.Dialog;
 import android.app.Fragment;
 import android.app.backup.BackupManager;
 import android.content.Context;
@@ -34,31 +33,20 @@
 import android.preference.Preference.OnPreferenceClickListener;
 import android.preference.PreferenceGroup;
 import android.preference.PreferenceScreen;
-import android.text.TextUtils;
-import android.text.method.LinkMovementMethod;
-import android.util.Log;
 import android.view.View;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 import android.widget.TextView;
 
-import com.android.inputmethod.compat.CompatUtils;
-import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
-import com.android.inputmethod.latin.VibratorUtils;
 import com.android.inputmethod.latin.define.ProductionFlag;
 import com.android.inputmethodcommon.InputMethodSettingsActivity;
 
-import java.util.Locale;
-
 public class Settings extends InputMethodSettingsActivity
-        implements SharedPreferences.OnSharedPreferenceChangeListener, OnPreferenceClickListener {
-    private static final String TAG = Settings.class.getSimpleName();
-
+        implements SharedPreferences.OnSharedPreferenceChangeListener {
     public static final boolean ENABLE_EXPERIMENTAL_SETTINGS = false;
 
     // In the same order as xml/prefs.xml
     public static final String PREF_GENERAL_SETTINGS = "general_settings";
-    public static final String PREF_SUBTYPES_SETTINGS = "subtype_settings";
     public static final String PREF_AUTO_CAP = "auto_cap";
     public static final String PREF_VIBRATE_ON = "vibrate_on";
     public static final String PREF_SOUND_ON = "sound_on";
@@ -90,7 +78,6 @@
     public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
     public static final String PREF_DEBUG_SETTINGS = "debug_settings";
 
-    private PreferenceScreen mInputLanguageSelection;
     private PreferenceScreen mKeypressVibrationDurationSettingsPref;
     private PreferenceScreen mKeypressSoundVolumeSettingsPref;
     private ListPreference mVoicePreference;
@@ -102,14 +89,10 @@
     // Prediction: use bigrams to predict the next word when there is no input for it yet
     private CheckBoxPreference mBigramPrediction;
     private Preference mDebugSettingsPreference;
-    private boolean mVoiceOn;
 
-    private AlertDialog mDialog;
     private TextView mKeypressVibrationDurationSettingsTextView;
     private TextView mKeypressSoundVolumeSettingsTextView;
 
-    private String mVoiceModeOff;
-
     private void ensureConsistencyOfAutoCorrectionSettings() {
         final String autoCorrectionOff = getResources().getString(
                 R.string.auto_correction_threshold_mode_index_off);
@@ -140,18 +123,12 @@
         final Context context = getActivityInternal();
 
         addPreferencesFromResource(R.xml.prefs);
-        mInputLanguageSelection = (PreferenceScreen) findPreference(PREF_SUBTYPES_SETTINGS);
-        mInputLanguageSelection.setOnPreferenceClickListener(this);
         mVoicePreference = (ListPreference) findPreference(PREF_VOICE_MODE);
         mShowCorrectionSuggestionsPreference =
                 (ListPreference) findPreference(PREF_SHOW_SUGGESTIONS_SETTING);
         SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
         prefs.registerOnSharedPreferenceChangeListener(this);
 
-        mVoiceModeOff = getString(R.string.voice_mode_off);
-        mVoiceOn = !(prefs.getString(PREF_VOICE_MODE, mVoiceModeOff)
-                .equals(mVoiceModeOff));
-
         mAutoCorrectionThresholdPreference =
                 (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD);
         mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTION);
@@ -183,10 +160,6 @@
             generalSettings.removePreference(findPreference(PREF_VIBRATE_ON));
         }
 
-        if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) {
-            generalSettings.removePreference(findPreference(PREF_SUBTYPES_SETTINGS));
-        }
-
         final boolean showPopupOption = res.getBoolean(
                 R.bool.config_enable_show_popup_on_keypress_option);
         if (!showPopupOption) {
@@ -318,25 +291,12 @@
                     !SettingsValues.isLanguageSwitchKeySupressed(prefs));
         }
         ensureConsistencyOfAutoCorrectionSettings();
-        mVoiceOn = !(prefs.getString(PREF_VOICE_MODE, mVoiceModeOff)
-                .equals(mVoiceModeOff));
         updateVoiceModeSummary();
         updateShowCorrectionSuggestionsSummary();
         updateKeyPreviewPopupDelaySummary();
         refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources());
     }
 
-    @Override
-    public boolean onPreferenceClick(Preference pref) {
-        if (pref == mInputLanguageSelection) {
-            final String imeId = SubtypeUtils.getInputMethodId(
-                    getActivityInternal().getApplicationInfo().packageName);
-            startActivity(CompatUtils.getInputLanguageSelectionIntent(imeId, 0));
-            return true;
-        }
-        return false;
-    }
-
     private void updateShowCorrectionSuggestionsSummary() {
         mShowCorrectionSuggestionsPreference.setSummary(
                 getResources().getStringArray(R.array.prefs_suggestion_visibilities)
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index 0ad685b..f76cc7e 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -24,6 +24,7 @@
 
 import com.android.inputmethod.compat.InputTypeCompatUtils;
 import com.android.inputmethod.keyboard.internal.KeySpecParser;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.VibratorUtils;
 
 import java.util.ArrayList;
@@ -162,7 +163,7 @@
         if (puncs != null) {
             for (final String puncSpec : puncs) {
                 puncList.add(new SuggestedWords.SuggestedWordInfo(
-                        KeySpecParser.getLabel(puncSpec)));
+                        KeySpecParser.getLabel(puncSpec), SuggestedWordInfo.MAX_SCORE));
             }
         }
         return new SuggestedWords(puncList,
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index de2e8be..e353644 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -29,14 +29,13 @@
 import android.os.IBinder;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
 
-import com.android.inputmethod.compat.InputMethodInfoCompatWrapper;
 import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
-import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper;
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -47,7 +46,6 @@
 
     public static final String KEYBOARD_MODE = "keyboard";
     private static final char LOCALE_SEPARATOR = '_';
-    private static final String VOICE_MODE = "voice";
     private static final String SUBTYPE_EXTRAVALUE_REQUIRE_NETWORK_CONNECTIVITY =
             "requireNetworkConnectivity";
 
@@ -59,19 +57,19 @@
     private /* final */ InputMethodManagerCompatWrapper mImm;
     private /* final */ Resources mResources;
     private /* final */ ConnectivityManager mConnectivityManager;
-    private final ArrayList<InputMethodSubtypeCompatWrapper>
-            mEnabledKeyboardSubtypesOfCurrentInputMethod =
-                    new ArrayList<InputMethodSubtypeCompatWrapper>();
+    private final ArrayList<InputMethodSubtype> mEnabledKeyboardSubtypesOfCurrentInputMethod =
+            new ArrayList<InputMethodSubtype>();
     private final ArrayList<String> mEnabledLanguagesOfCurrentInputMethod = new ArrayList<String>();
 
     /*-----------------------------------------------------------*/
     // Variants which should be changed only by reload functions.
     private boolean mNeedsToDisplayLanguage;
     private boolean mIsSystemLanguageSameAsInputLanguage;
-    private InputMethodInfoCompatWrapper mShortcutInputMethodInfo;
-    private InputMethodSubtypeCompatWrapper mShortcutSubtype;
-    private List<InputMethodSubtypeCompatWrapper> mAllEnabledSubtypesOfCurrentInputMethod;
-    private InputMethodSubtypeCompatWrapper mCurrentSubtype;
+    private InputMethodInfo mShortcutInputMethodInfo;
+    private InputMethodSubtype mShortcutSubtype;
+    private List<InputMethodSubtype> mAllEnabledSubtypesOfCurrentInputMethod;
+    // Note: This variable is always non-null after {@link #initialize(LatinIME)}.
+    private InputMethodSubtype mCurrentSubtype;
     private Locale mSystemLocale;
     private Locale mInputLocale;
     private String mInputLocaleStr;
@@ -104,7 +102,7 @@
         mSystemLocale = null;
         mInputLocale = null;
         mInputLocaleStr = null;
-        mCurrentSubtype = null;
+        mCurrentSubtype = mImm.getCurrentInputMethodSubtype();
         mAllEnabledSubtypesOfCurrentInputMethod = null;
 
         final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
@@ -134,7 +132,7 @@
                 null, true);
         mEnabledLanguagesOfCurrentInputMethod.clear();
         mEnabledKeyboardSubtypesOfCurrentInputMethod.clear();
-        for (InputMethodSubtypeCompatWrapper ims : mAllEnabledSubtypesOfCurrentInputMethod) {
+        for (InputMethodSubtype ims : mAllEnabledSubtypesOfCurrentInputMethod) {
             final String locale = getSubtypeLocale(ims);
             final String mode = ims.getMode();
             mLocaleSplitter.setString(locale);
@@ -168,12 +166,12 @@
                             + ", " + mShortcutSubtype.getMode())));
         }
         // TODO: Update an icon for shortcut IME
-        final Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>> shortcuts =
+        final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
                 mImm.getShortcutInputMethodsAndSubtypes();
         mShortcutInputMethodInfo = null;
         mShortcutSubtype = null;
-        for (InputMethodInfoCompatWrapper imi : shortcuts.keySet()) {
-            List<InputMethodSubtypeCompatWrapper> subtypes = shortcuts.get(imi);
+        for (InputMethodInfo imi : shortcuts.keySet()) {
+            List<InputMethodSubtype> subtypes = shortcuts.get(imi);
             // TODO: Returns the first found IMI for now. Should handle all shortcuts as
             // appropriate.
             mShortcutInputMethodInfo = imi;
@@ -191,27 +189,17 @@
         }
     }
 
-    private static String getSubtypeLocale(InputMethodSubtypeCompatWrapper subtype) {
+    private static String getSubtypeLocale(InputMethodSubtype subtype) {
         final String keyboardLocale = subtype.getExtraValueOf(
                 LatinIME.SUBTYPE_EXTRA_VALUE_KEYBOARD_LOCALE);
         return keyboardLocale != null ? keyboardLocale : subtype.getLocale();
     }
 
     // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
-    public void updateSubtype(InputMethodSubtypeCompatWrapper newSubtype) {
-        final String newLocale;
-        final String newMode;
+    public void updateSubtype(InputMethodSubtype newSubtype) {
+        final String newLocale = getSubtypeLocale(newSubtype);
+        final String newMode = newSubtype.getMode();
         final String oldMode = getCurrentSubtypeMode();
-        if (newSubtype == null) {
-            // Normally, newSubtype shouldn't be null. But just in case newSubtype was null,
-            // fallback to the default locale.
-            Log.w(TAG, "Couldn't get the current subtype.");
-            newLocale = "en_US";
-            newMode = KEYBOARD_MODE;
-        } else {
-            newLocale = getSubtypeLocale(newSubtype);
-            newMode = newSubtype.getMode();
-        }
         if (DBG) {
             Log.w(TAG, "Update subtype to:" + newLocale + "," + newMode
                     + ", from: " + mInputLocaleStr + ", " + oldMode);
@@ -286,12 +274,10 @@
         }
 
         final String imiId = mShortcutInputMethodInfo.getId();
-        final InputMethodSubtypeCompatWrapper subtype = mShortcutSubtype;
-        switchToTargetIME(imiId, subtype);
+        switchToTargetIME(imiId, mShortcutSubtype);
     }
 
-    private void switchToTargetIME(
-            final String imiId, final InputMethodSubtypeCompatWrapper subtype) {
+    private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype) {
         final IBinder token = mService.getWindow().getWindow().getAttributes().token;
         if (token == null) {
             return;
@@ -302,17 +288,6 @@
                 mImm.setInputMethodAndSubtype(token, imiId, subtype);
                 return null;
             }
-
-            @Override
-            protected void onPostExecute(Void result) {
-                // Calls in this method need to be done in the same thread as the thread which
-                // called switchToShortcutIME().
-
-                // Notify an event that the current subtype was changed. This event will be
-                // handled if "onCurrentInputMethodSubtypeChanged" can't be implemented
-                // when the API level is 10 or previous.
-                mService.notifyOnCurrentInputMethodSubtypeChanged(subtype);
-            }
         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 
@@ -320,8 +295,7 @@
         return getSubtypeIcon(mShortcutInputMethodInfo, mShortcutSubtype);
     }
 
-    private Drawable getSubtypeIcon(
-            InputMethodInfoCompatWrapper imi, InputMethodSubtypeCompatWrapper subtype) {
+    private Drawable getSubtypeIcon(InputMethodInfo imi, InputMethodSubtype subtype) {
         final PackageManager pm = mService.getPackageManager();
         if (imi != null) {
             final String imiPackageName = imi.getPackageName();
@@ -362,15 +336,9 @@
         if (mShortcutSubtype == null) {
             return true;
         }
-        // For compatibility, if the shortcut subtype is dummy, we assume the shortcut IME
-        // (built-in voice dummy subtype) is available.
-        if (!mShortcutSubtype.hasOriginalObject()) {
-            return true;
-        }
         final boolean allowsImplicitlySelectedSubtypes = true;
-        for (final InputMethodSubtypeCompatWrapper enabledSubtype :
-                mImm.getEnabledInputMethodSubtypeList(
-                        mShortcutInputMethodInfo, allowsImplicitlySelectedSubtypes)) {
+        for (final InputMethodSubtype enabledSubtype : mImm.getEnabledInputMethodSubtypeList(
+                mShortcutInputMethodInfo, allowsImplicitlySelectedSubtypes)) {
             if (enabledSubtype.equals(mShortcutSubtype)) {
                 return true;
             }
@@ -462,20 +430,20 @@
 
     public String getCurrentSubtypeExtraValue() {
         // If null, return what an empty ExtraValue would return : the empty string.
-        return null != mCurrentSubtype ? mCurrentSubtype.getExtraValue() : "";
+        return mCurrentSubtype.getExtraValue();
     }
 
     public boolean currentSubtypeContainsExtraValueKey(String key) {
         // If null, return what an empty ExtraValue would return : false.
-        return null != mCurrentSubtype ? mCurrentSubtype.containsExtraValueKey(key) : false;
+        return mCurrentSubtype.containsExtraValueKey(key);
     }
 
     public String getCurrentSubtypeExtraValueOf(String key) {
         // If null, return what an empty ExtraValue would return : null.
-        return null != mCurrentSubtype ? mCurrentSubtype.getExtraValueOf(key) : null;
+        return mCurrentSubtype.getExtraValueOf(key);
     }
 
     public String getCurrentSubtypeMode() {
-        return null != mCurrentSubtype ? mCurrentSubtype.getMode() : KEYBOARD_MODE;
+        return mCurrentSubtype.getMode();
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/SubtypeUtils.java b/java/src/com/android/inputmethod/latin/SubtypeUtils.java
index cb2bcf4..2c5d582 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeUtils.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeUtils.java
@@ -17,10 +17,10 @@
 package com.android.inputmethod.latin;
 
 import android.content.Context;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
 
-import com.android.inputmethod.compat.InputMethodInfoCompatWrapper;
 import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
-import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper;
 
 import java.util.Collections;
 import java.util.List;
@@ -31,15 +31,13 @@
     }
 
     // TODO: Cache my InputMethodInfo and/or InputMethodSubtype list.
-    public static boolean checkIfSubtypeBelongsToThisIme(Context context,
-            InputMethodSubtypeCompatWrapper ims) {
+    public static boolean checkIfSubtypeBelongsToThisIme(Context context, InputMethodSubtype ims) {
         final InputMethodManagerCompatWrapper imm = InputMethodManagerCompatWrapper.getInstance();
         if (imm == null) return false;
 
-        final InputMethodInfoCompatWrapper myImi = getInputMethodInfo(context.getPackageName());
-        final List<InputMethodSubtypeCompatWrapper> subtypes =
-                imm.getEnabledInputMethodSubtypeList(myImi, true);
-        for (final InputMethodSubtypeCompatWrapper subtype : subtypes) {
+        final InputMethodInfo myImi = getInputMethodInfo(context.getPackageName());
+        final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(myImi, true);
+        for (final InputMethodSubtype subtype : subtypes) {
             if (subtype.equals(ims)) {
                 return true;
             }
@@ -52,29 +50,29 @@
         final InputMethodManagerCompatWrapper imm = InputMethodManagerCompatWrapper.getInstance();
         if (imm == null) return false;
 
-        final List<InputMethodInfoCompatWrapper> enabledImis = imm.getEnabledInputMethodList();
+        final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList();
         return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, enabledImis);
     }
 
     public static boolean hasMultipleEnabledSubtypesInThisIme(Context context,
             final boolean shouldIncludeAuxiliarySubtypes) {
-        final InputMethodInfoCompatWrapper myImi = getInputMethodInfo(context.getPackageName());
-        final List<InputMethodInfoCompatWrapper> imiList = Collections.singletonList(myImi);
+        final InputMethodInfo myImi = getInputMethodInfo(context.getPackageName());
+        final List<InputMethodInfo> imiList = Collections.singletonList(myImi);
         return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, imiList);
     }
 
     private static boolean hasMultipleEnabledSubtypes(final boolean shouldIncludeAuxiliarySubtypes,
-            List<InputMethodInfoCompatWrapper> imiList) {
+            List<InputMethodInfo> imiList) {
         final InputMethodManagerCompatWrapper imm = InputMethodManagerCompatWrapper.getInstance();
         if (imm == null) return false;
 
         // Number of the filtered IMEs
         int filteredImisCount = 0;
 
-        for (InputMethodInfoCompatWrapper imi : imiList) {
+        for (InputMethodInfo imi : imiList) {
             // We can return true immediately after we find two or more filtered IMEs.
             if (filteredImisCount > 1) return true;
-            final List<InputMethodSubtypeCompatWrapper> subtypes =
+            final List<InputMethodSubtype> subtypes =
                     imm.getEnabledInputMethodSubtypeList(imi, true);
             // IMEs that have no subtypes should be counted.
             if (subtypes.isEmpty()) {
@@ -83,7 +81,7 @@
             }
 
             int auxCount = 0;
-            for (InputMethodSubtypeCompatWrapper subtype : subtypes) {
+            for (InputMethodSubtype subtype : subtypes) {
                 if (subtype.isAuxiliary()) {
                     ++auxCount;
                 }
@@ -102,13 +100,12 @@
         if (filteredImisCount > 1) {
             return true;
         }
-        final List<InputMethodSubtypeCompatWrapper> subtypes =
-                imm.getEnabledInputMethodSubtypeList(null, true);
+        final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(null, true);
         int keyboardCount = 0;
         // imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's
         // both explicitly and implicitly enabled input method subtype.
         // (The current IME should be LatinIME.)
-        for (InputMethodSubtypeCompatWrapper subtype : subtypes) {
+        for (InputMethodSubtype subtype : subtypes) {
             if (SubtypeSwitcher.KEYBOARD_MODE.equals(subtype.getMode())) {
                 ++keyboardCount;
             }
@@ -120,13 +117,13 @@
         return getInputMethodInfo(packageName).getId();
     }
 
-    public static InputMethodInfoCompatWrapper getInputMethodInfo(String packageName) {
+    public static InputMethodInfo getInputMethodInfo(String packageName) {
         final InputMethodManagerCompatWrapper imm = InputMethodManagerCompatWrapper.getInstance();
         if (imm == null) {
             throw new RuntimeException("Input method manager not found");
         }
 
-        for (final InputMethodInfoCompatWrapper imi : imm.getEnabledInputMethodList()) {
+        for (final InputMethodInfo imi : imm.getEnabledInputMethodList()) {
             if (imi.getPackageName().equals(packageName))
                 return imi;
         }
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 9ae2506..b31f301 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -26,7 +26,6 @@
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Locale;
@@ -93,11 +92,9 @@
     private static final int PREF_MAX_BIGRAMS = 60;
 
     private double mAutoCorrectionThreshold;
-    private int[] mScores = new int[mPrefMaxSuggestions];
-    private int[] mBigramScores = new int[PREF_MAX_BIGRAMS];
 
-    private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
-    private ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>();
+    private ArrayList<SuggestedWordInfo> mSuggestions = new ArrayList<SuggestedWordInfo>();
+    private ArrayList<SuggestedWordInfo> mBigramSuggestions = new ArrayList<SuggestedWordInfo>();
     private CharSequence mConsideredWord;
 
     // TODO: Remove these member variables by passing more context to addWord() callback method
@@ -230,10 +227,8 @@
         return sb;
     }
 
-    protected void addBigramToSuggestions(CharSequence bigram) {
-        final StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
-        sb.append(bigram);
-        mSuggestions.add(sb);
+    protected void addBigramToSuggestions(SuggestedWordInfo bigram) {
+        mSuggestions.add(bigram);
     }
 
     private static final WordComposer sEmptyWordComposer = new WordComposer();
@@ -242,15 +237,13 @@
         mIsFirstCharCapitalized = false;
         mIsAllUpperCase = false;
         mTrailingSingleQuotesCount = 0;
-        mSuggestions = new ArrayList<CharSequence>(mPrefMaxSuggestions);
-        Arrays.fill(mScores, 0);
+        mSuggestions = new ArrayList<SuggestedWordInfo>(mPrefMaxSuggestions);
 
         // Treating USER_TYPED as UNIGRAM suggestion for logging now.
         LatinImeLogger.onAddSuggestedWord("", Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM);
         mConsideredWord = "";
 
-        Arrays.fill(mBigramScores, 0);
-        mBigramSuggestions = new ArrayList<CharSequence>(PREF_MAX_BIGRAMS);
+        mBigramSuggestions = new ArrayList<SuggestedWordInfo>(PREF_MAX_BIGRAMS);
 
         CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase();
         if (mMainDict != null && mMainDict.isValidWord(lowerPrevWord)) {
@@ -265,9 +258,9 @@
             addBigramToSuggestions(mBigramSuggestions.get(i));
         }
 
-        StringUtils.removeDupes(mSuggestions);
+        SuggestedWordInfo.removeDups(mSuggestions);
 
-        return new SuggestedWords(SuggestedWords.getFromCharSequenceList(mSuggestions),
+        return new SuggestedWords(mSuggestions,
                 false /* typedWordValid */,
                 false /* hasAutoCorrectionCandidate */,
                 false /* allowsToBeAutoCorrected */,
@@ -283,8 +276,7 @@
         mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
         mIsAllUpperCase = wordComposer.isAllUpperCase();
         mTrailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
-        mSuggestions = new ArrayList<CharSequence>(mPrefMaxSuggestions);
-        Arrays.fill(mScores, 0);
+        mSuggestions = new ArrayList<SuggestedWordInfo>(mPrefMaxSuggestions);
 
         final String typedWord = wordComposer.getTypedWord();
         final String consideredWord = mTrailingSingleQuotesCount > 0
@@ -296,8 +288,7 @@
 
         if (wordComposer.size() <= 1 && (correctionMode == CORRECTION_FULL_BIGRAM)) {
             // At first character typed, search only the bigrams
-            Arrays.fill(mBigramScores, 0);
-            mBigramSuggestions = new ArrayList<CharSequence>(PREF_MAX_BIGRAMS);
+            mBigramSuggestions = new ArrayList<SuggestedWordInfo>(PREF_MAX_BIGRAMS);
 
             if (!TextUtils.isEmpty(prevWordForBigram)) {
                 CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase();
@@ -317,12 +308,14 @@
                     // Word entered: return only bigrams that match the first char of the typed word
                     final char currentChar = consideredWord.charAt(0);
                     // TODO: Must pay attention to locale when changing case.
+                    // TODO: Use codepoint instead of char
                     final char currentCharUpper = Character.toUpperCase(currentChar);
                     int count = 0;
                     final int bigramSuggestionSize = mBigramSuggestions.size();
                     for (int i = 0; i < bigramSuggestionSize; i++) {
-                        final CharSequence bigramSuggestion = mBigramSuggestions.get(i);
-                        final char bigramSuggestionFirstChar = bigramSuggestion.charAt(0);
+                        final SuggestedWordInfo bigramSuggestion = mBigramSuggestions.get(i);
+                        final char bigramSuggestionFirstChar =
+                                (char)bigramSuggestion.codePointAt(0);
                         if (bigramSuggestionFirstChar == currentChar
                                 || bigramSuggestionFirstChar == currentCharUpper) {
                             addBigramToSuggestions(bigramSuggestion);
@@ -359,7 +352,7 @@
         if (CORRECTION_FULL == correctionMode || CORRECTION_FULL_BIGRAM == correctionMode) {
             final CharSequence autoCorrection =
                     AutoCorrection.computeAutoCorrectionWord(mUnigramDictionaries, wordComposer,
-                            mSuggestions, mScores, consideredWord, mAutoCorrectionThreshold,
+                            mSuggestions, consideredWord, mAutoCorrectionThreshold,
                             whitelistedWord);
             hasAutoCorrection = (null != autoCorrection);
         } else {
@@ -372,20 +365,22 @@
                 for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
                     sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
                 }
-                mSuggestions.add(0, sb.toString());
+                mSuggestions.add(0, new SuggestedWordInfo(
+                        sb.toString(), SuggestedWordInfo.MAX_SCORE));
             } else {
-                mSuggestions.add(0, whitelistedWord);
+                mSuggestions.add(0, new SuggestedWordInfo(
+                        whitelistedWord, SuggestedWordInfo.MAX_SCORE));
             }
         }
 
-        mSuggestions.add(0, typedWord);
-        StringUtils.removeDupes(mSuggestions);
+        mSuggestions.add(0, new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE));
+        SuggestedWordInfo.removeDups(mSuggestions);
 
         final ArrayList<SuggestedWordInfo> suggestionsList;
         if (DBG) {
-            suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, mSuggestions, mScores);
+            suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, mSuggestions);
         } else {
-            suggestionsList = SuggestedWords.getFromCharSequenceList(mSuggestions);
+            suggestionsList = mSuggestions;
         }
 
         // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
@@ -415,50 +410,45 @@
                 false /* isObsoleteSuggestions */);
     }
 
-    // This assumes the scores[] array is at least as long as suggestions.size() - 1.
     private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo(
-            final String typedWord, final ArrayList<CharSequence> suggestions, final int[] scores) {
-        // TODO: this doesn't take into account the fact that removing dupes from mSuggestions
-        // may have made mScores[] and mSuggestions out of sync.
-        final CharSequence autoCorrectionSuggestion = suggestions.get(0);
+            final String typedWord, final ArrayList<SuggestedWordInfo> suggestions) {
+        final SuggestedWordInfo typedWordInfo = suggestions.get(0);
+        typedWordInfo.setDebugString("+");
         double normalizedScore = BinaryDictionary.calcNormalizedScore(
-                typedWord, autoCorrectionSuggestion.toString(), scores[0]);
+                typedWord, typedWordInfo.toString(), typedWordInfo.mScore);
         final int suggestionsSize = suggestions.size();
         final ArrayList<SuggestedWordInfo> suggestionsList =
                 new ArrayList<SuggestedWordInfo>(suggestionsSize);
-        suggestionsList.add(new SuggestedWordInfo(autoCorrectionSuggestion, "+"));
+        suggestionsList.add(typedWordInfo);
         // Note: i here is the index in mScores[], but the index in mSuggestions is one more
         // than i because we added the typed word to mSuggestions without touching mScores.
-        for (int i = 0; i < scores.length && i < suggestionsSize - 1; ++i) {
+        for (int i = 0; i < suggestionsSize - 1; ++i) {
+            final SuggestedWordInfo cur = suggestions.get(i + 1);
             final String scoreInfoString;
             if (normalizedScore > 0) {
-                scoreInfoString = String.format("%d (%4.2f)", scores[i], normalizedScore);
+                scoreInfoString = String.format("%d (%4.2f)", cur.mScore, normalizedScore);
                 normalizedScore = 0.0;
             } else {
-                scoreInfoString = Integer.toString(scores[i]);
+                scoreInfoString = Integer.toString(cur.mScore);
             }
-            suggestionsList.add(new SuggestedWordInfo(suggestions.get(i + 1), scoreInfoString));
-        }
-        for (int i = scores.length; i < suggestionsSize; ++i) {
-            suggestionsList.add(new SuggestedWordInfo(suggestions.get(i), "--"));
+            cur.setDebugString(scoreInfoString);
+            suggestionsList.add(cur);
         }
         return suggestionsList;
     }
 
+    // TODO: Use codepoint instead of char
     @Override
     public boolean addWord(final char[] word, final int offset, final int length, int score,
             final int dicTypeId, final int dataType) {
         int dataTypeForLog = dataType;
-        final ArrayList<CharSequence> suggestions;
-        final int[] sortedScores;
+        final ArrayList<SuggestedWordInfo> suggestions;
         final int prefMaxSuggestions;
         if (dataType == Dictionary.BIGRAM) {
             suggestions = mBigramSuggestions;
-            sortedScores = mBigramScores;
             prefMaxSuggestions = PREF_MAX_BIGRAMS;
         } else {
             suggestions = mSuggestions;
-            sortedScores = mScores;
             prefMaxSuggestions = mPrefMaxSuggestions;
         }
 
@@ -469,13 +459,13 @@
             // TODO: remove this surrounding if clause and move this logic to
             // getSuggestedWordBuilder.
             if (suggestions.size() > 0) {
-                final String currentHighestWord = suggestions.get(0).toString();
+                final SuggestedWordInfo currentHighestWord = suggestions.get(0);
                 // If the current highest word is also equal to typed word, we need to compare
                 // frequency to determine the insertion position. This does not ensure strictly
                 // correct ordering, but ensures the top score is on top which is enough for
                 // removing duplicates correctly.
-                if (StringUtils.equalsIgnoreCase(currentHighestWord, word, offset, length)
-                        && score <= sortedScores[0]) {
+                if (StringUtils.equalsIgnoreCase(currentHighestWord.mWord, word, offset, length)
+                        && score <= currentHighestWord.mScore) {
                     pos = 1;
                 }
             }
@@ -486,7 +476,7 @@
                 if(bigramSuggestion >= 0) {
                     dataTypeForLog = Dictionary.BIGRAM;
                     // turn freq from bigram into multiplier specified above
-                    double multiplier = (((double) mBigramScores[bigramSuggestion])
+                    double multiplier = (((double) mBigramSuggestions.get(bigramSuggestion).mScore)
                             / MAXIMUM_BIGRAM_FREQUENCY)
                             * (BIGRAM_MULTIPLIER_MAX - BIGRAM_MULTIPLIER_MIN)
                             + BIGRAM_MULTIPLIER_MIN;
@@ -500,10 +490,12 @@
             }
 
             // Check the last one's score and bail
-            if (sortedScores[prefMaxSuggestions - 1] >= score) return true;
-            while (pos < prefMaxSuggestions) {
-                if (sortedScores[pos] < score
-                        || (sortedScores[pos] == score && length < suggestions.get(pos).length())) {
+            if (suggestions.size() >= prefMaxSuggestions
+                    && suggestions.get(prefMaxSuggestions - 1).mScore >= score) return true;
+            while (pos < suggestions.size()) {
+                final int curScore = suggestions.get(pos).mScore;
+                if (curScore < score
+                        || (curScore == score && length < suggestions.get(pos).codePointCount())) {
                     break;
                 }
                 pos++;
@@ -513,8 +505,6 @@
             return true;
         }
 
-        System.arraycopy(sortedScores, pos, sortedScores, pos + 1, prefMaxSuggestions - pos - 1);
-        sortedScores[pos] = score;
         final StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
         // TODO: Must pay attention to locale when changing case.
         if (mIsAllUpperCase) {
@@ -530,7 +520,7 @@
         for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
             sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
         }
-        suggestions.add(pos, sb);
+        suggestions.add(pos, new SuggestedWordInfo(sb, score));
         if (suggestions.size() > prefMaxSuggestions) {
             suggestions.remove(prefMaxSuggestions);
         } else {
@@ -539,15 +529,16 @@
         return true;
     }
 
+    // TODO: Use codepoint instead of char
     private int searchBigramSuggestion(final char[] word, final int offset, final int length) {
         // TODO This is almost O(n^2). Might need fix.
         // search whether the word appeared in bigram data
         int bigramSuggestSize = mBigramSuggestions.size();
         for (int i = 0; i < bigramSuggestSize; i++) {
-            if (mBigramSuggestions.get(i).length() == length) {
+            if (mBigramSuggestions.get(i).codePointCount() == length) {
                 boolean chk = true;
                 for (int j = 0; j < length; j++) {
-                    if (mBigramSuggestions.get(i).charAt(j) != word[offset+j]) {
+                    if (mBigramSuggestions.get(i).codePointAt(j) != word[offset+j]) {
                         chk = false;
                         break;
                     }
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index ef8e58e..0c0ce18 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -56,6 +56,10 @@
         return mSuggestedWordInfoList.get(pos).mWord;
     }
 
+    public SuggestedWordInfo getWordInfo(int pos) {
+        return mSuggestedWordInfoList.get(pos);
+    }
+
     public SuggestedWordInfo getInfo(int pos) {
         return mSuggestedWordInfoList.get(pos);
     }
@@ -79,20 +83,12 @@
                 + " words=" + Arrays.toString(mSuggestedWordInfoList.toArray());
     }
 
-    public static ArrayList<SuggestedWordInfo> getFromCharSequenceList(
-            final ArrayList<CharSequence> wordList) {
-        final ArrayList<SuggestedWordInfo> result = new ArrayList<SuggestedWordInfo>();
-        for (CharSequence word : wordList) {
-            if (null != word) result.add(new SuggestedWordInfo(word));
-        }
-        return result;
-    }
-
     public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions(
             final CompletionInfo[] infos) {
         final ArrayList<SuggestedWordInfo> result = new ArrayList<SuggestedWordInfo>();
         for (CompletionInfo info : infos) {
-            if (null != info) result.add(new SuggestedWordInfo(info.getText()));
+            if (null != info) result.add(new SuggestedWordInfo(info.getText(),
+                    SuggestedWordInfo.MAX_SCORE));
         }
         return result;
     }
@@ -103,14 +99,15 @@
             final CharSequence typedWord, final SuggestedWords previousSuggestions) {
         final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<SuggestedWordInfo>();
         final HashSet<String> alreadySeen = new HashSet<String>();
-        suggestionsList.add(new SuggestedWordInfo(typedWord));
+        suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE));
         alreadySeen.add(typedWord.toString());
         final int previousSize = previousSuggestions.size();
         for (int pos = 1; pos < previousSize; pos++) {
-            final String prevWord = previousSuggestions.getWord(pos).toString();
+            final SuggestedWordInfo prevWordInfo = previousSuggestions.getWordInfo(pos);
+            final String prevWord = prevWordInfo.mWord.toString();
             // Filter out duplicate suggestion.
             if (!alreadySeen.contains(prevWord)) {
-                suggestionsList.add(new SuggestedWordInfo(prevWord));
+                suggestionsList.add(prevWordInfo);
                 alreadySeen.add(prevWord);
             }
         }
@@ -118,30 +115,64 @@
     }
 
     public static class SuggestedWordInfo {
+        public static final int MAX_SCORE = Integer.MAX_VALUE;
+        private final String mWordStr;
         public final CharSequence mWord;
-        private final String mDebugString;
+        public final int mScore;
+        public final int mCodePointCount;
+        private String mDebugString = "";
 
-        public SuggestedWordInfo(final CharSequence word) {
+        public SuggestedWordInfo(final CharSequence word, final int score) {
+            mWordStr = word.toString();
             mWord = word;
-            mDebugString = "";
+            mScore = score;
+            mCodePointCount = mWordStr.codePointCount(0, mWordStr.length());
         }
 
-        public SuggestedWordInfo(final CharSequence word, final String debugString) {
-            mWord = word;
-            if (null == debugString) throw new NullPointerException("");
-            mDebugString = debugString;
+
+        public void setDebugString(String str) {
+            if (null == str) throw new NullPointerException("Debug info is null");
+            mDebugString = str;
         }
 
         public String getDebugString() {
             return mDebugString;
         }
 
+        public int codePointCount() {
+            return mCodePointCount;
+        }
+
+        public int codePointAt(int i) {
+            return mWordStr.codePointAt(i);
+        }
+
         @Override
         public String toString() {
             if (TextUtils.isEmpty(mDebugString)) {
-                return mWord.toString();
+                return mWordStr;
             } else {
-                return mWord.toString() + " (" + mDebugString.toString() + ")";
+                return mWordStr + " (" + mDebugString.toString() + ")";
+            }
+        }
+
+        // TODO: Consolidate this method and StringUtils.removeDupes() in the future.
+        public static void removeDups(ArrayList<SuggestedWordInfo> candidates) {
+            if (candidates.size() <= 1) {
+                return;
+            }
+            int i = 1;
+            while(i < candidates.size()) {
+                final SuggestedWordInfo cur = candidates.get(i);
+                for (int j = 0; j < i; ++j) {
+                    final SuggestedWordInfo previous = candidates.get(j);
+                    if (TextUtils.equals(cur.mWord, previous.mWord)) {
+                        candidates.remove(cur.mScore < previous.mScore ? i : j);
+                        --i;
+                        break;
+                    }
+                }
+                ++i;
             }
         }
     }
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index af7f863..010ea68 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -26,6 +26,7 @@
 import java.io.RandomAccessFile;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.TreeMap;
@@ -922,7 +923,14 @@
         if (version >= FIRST_VERSION_WITH_HEADER_SIZE) {
             final int headerSizeOffset = index;
             index += 4; // Size of the header size
-            // TODO: Write out the header contents here.
+
+            // Write out the options.
+            for (final String key : dict.mOptions.mAttributes.keySet()) {
+                final String value = dict.mOptions.mAttributes.get(key);
+                index += CharEncoding.writeString(buffer, index, key);
+                index += CharEncoding.writeString(buffer, index, value);
+            }
+
             // Write out the header size.
             buffer[headerSizeOffset] = (byte) (0xFF & (index >> 24));
             buffer[headerSizeOffset + 1] = (byte) (0xFF & (index >> 16));
@@ -1214,12 +1222,17 @@
         source.readUnsignedShort();
 
         final long headerSize;
+        final HashMap<String, String> options = new HashMap<String, String>();
         if (version < FIRST_VERSION_WITH_HEADER_SIZE) {
             headerSize = source.getFilePointer();
         } else {
             headerSize = (source.readUnsignedByte() << 24) + (source.readUnsignedByte() << 16)
                     + (source.readUnsignedByte() << 8) + source.readUnsignedByte();
-            // read the header body
+            while (source.getFilePointer() < headerSize) {
+                final String key = CharEncoding.readString(source);
+                final String value = CharEncoding.readString(source);
+                options.put(key, value);
+            }
             source.seek(headerSize);
         }
 
@@ -1228,7 +1241,7 @@
         final Node root = readNode(source, headerSize, reverseNodeMapping, reverseGroupMapping);
 
         FusionDictionary newDict = new FusionDictionary(root,
-                new FusionDictionary.DictionaryOptions());
+                new FusionDictionary.DictionaryOptions(options));
         if (null != dict) {
             for (Word w : dict) {
                 newDict.add(w.mWord, w.mFrequency, w.mShortcutTargets, w.mBigrams);
diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index d3ffb47..99b1704 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -19,6 +19,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 
@@ -250,6 +251,10 @@
      * There are no options at the moment, so this class is empty.
      */
     public static class DictionaryOptions {
+        final HashMap<String, String> mAttributes;
+        public DictionaryOptions(final HashMap<String, String> attributes) {
+            mAttributes = attributes;
+        }
     }
 
 
@@ -257,8 +262,13 @@
     public final Node mRoot;
 
     public FusionDictionary() {
-        mOptions = new DictionaryOptions();
         mRoot = new Node();
+        mOptions = new DictionaryOptions(new HashMap<String, String>());
+    }
+
+    public FusionDictionary(final HashMap<String, String> attributes) {
+        mRoot = new Node();
+        mOptions = new DictionaryOptions(attributes);
     }
 
     public FusionDictionary(final Node root, final DictionaryOptions options) {
@@ -266,6 +276,10 @@
         mOptions = options;
     }
 
+    public void addOptionAttribute(final String key, final String value) {
+        mOptions.mAttributes.put(key, value);
+    }
+
     /**
      * Helper method to convert a String to an int array.
      */
diff --git a/tests/src/com/android/inputmethod/latin/ArbitrarySubtype.java b/tests/src/com/android/inputmethod/latin/ArbitrarySubtype.java
deleted file mode 100644
index 1e3482c..0000000
--- a/tests/src/com/android/inputmethod/latin/ArbitrarySubtype.java
+++ /dev/null
@@ -1,54 +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.
- */
-
-package com.android.inputmethod.latin;
-
-import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper;
-
-public class ArbitrarySubtype extends InputMethodSubtypeCompatWrapper {
-    final String mLocale;
-    final String mExtraValue;
-
-    public ArbitrarySubtype(final String locale, final String extraValue) {
-        super(locale);
-        mLocale = locale;
-        mExtraValue = extraValue;
-    }
-
-    public String getLocale() {
-        return mLocale;
-    }
-
-    public String getExtraValue() {
-        return mExtraValue;
-    }
-
-    public String getMode() {
-        return "keyboard";
-    }
-
-    public String getExtraValueOf(final String key) {
-        if (LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE.equals(key)) {
-            return "";
-        } else {
-            return null;
-        }
-    }
-
-    public boolean containsExtraValueKey(final String key) {
-        return LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE.equals(key);
-    }
-}
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index 7d97eed..5f6b229 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -30,6 +30,9 @@
 import android.view.ViewGroup;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
@@ -37,6 +40,8 @@
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardActionListener;
 
+import java.util.HashMap;
+
 public class InputTestsBase extends ServiceTestCase<LatinIME> {
 
     private static final String PREF_DEBUG_MODE = "debug_mode";
@@ -48,6 +53,8 @@
     protected Keyboard mKeyboard;
     protected TextView mTextView;
     protected InputConnection mInputConnection;
+    private final HashMap<String, InputMethodSubtype> mSubtypeMap =
+            new HashMap<String, InputMethodSubtype>();
 
     // A helper class to ease span tests
     public static class Span {
@@ -108,6 +115,7 @@
         final boolean previousDebugSetting = setDebugMode(true);
         mLatinIME.onCreate();
         setDebugMode(previousDebugSetting);
+        initSubtypeMap();
         final EditorInfo ei = new EditorInfo();
         ei.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
         final InputConnection ic = mTextView.onCreateInputConnection(ei);
@@ -126,6 +134,23 @@
         changeLanguage("en_US");
     }
 
+    private void initSubtypeMap() {
+        final InputMethodManager imm = (InputMethodManager)mLatinIME.getSystemService(
+                Context.INPUT_METHOD_SERVICE);
+        final String packageName = mLatinIME.getPackageName();
+        for (final InputMethodInfo imi : imm.getEnabledInputMethodList()) {
+            if (imi.getPackageName().equals(packageName)) {
+                for (final InputMethodSubtype ims :
+                    imm.getEnabledInputMethodSubtypeList(imi, true)) {
+                    final String locale = ims.getLocale();
+                    mSubtypeMap.put(locale, ims);
+                }
+                return;
+            }
+        }
+        fail("LatinIME is disabled");
+    }
+
     // We need to run the messages added to the handler from LatinIME. The only way to do
     // that is to call Looper#loop() on the right looper, so we're going to get the looper
     // object and call #loop() here. The messages in the handler actually run on the UI
@@ -217,8 +242,11 @@
     }
 
     protected void changeLanguage(final String locale) {
-        SubtypeSwitcher.getInstance().updateSubtype(
-                new ArbitrarySubtype(locale, LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE));
+        final InputMethodSubtype subtype = mSubtypeMap.get(locale);
+        if (subtype == null) {
+            fail("InputMethodSubtype for locale " + locale + " is not enabled");
+        }
+        SubtypeSwitcher.getInstance().updateSubtype(subtype);
         waitForDictionaryToBeLoaded();
     }
 
diff --git a/tools/makedict/src/com/android/inputmethod/latin/makedict/XmlDictInputOutput.java b/tools/makedict/src/com/android/inputmethod/latin/makedict/XmlDictInputOutput.java
index 483473b..1d45fd2 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/makedict/XmlDictInputOutput.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/makedict/XmlDictInputOutput.java
@@ -40,6 +40,7 @@
  */
 public class XmlDictInputOutput {
 
+    private static final String ROOT_TAG = "wordlist";
     private static final String WORD_TAG = "w";
     private static final String BIGRAM_TAG = "bigram";
     private static final String SHORTCUT_TAG = "shortcut";
@@ -96,6 +97,11 @@
                         mFreq = Integer.parseInt(attrs.getValue(attrIndex));
                     }
                 }
+            } else if (ROOT_TAG.equals(localName)) {
+                for (int attrIndex = 0; attrIndex < attrs.getLength(); ++attrIndex) {
+                    final String attrName = attrs.getLocalName(attrIndex);
+                    mDictionary.mOptions.mAttributes.put(attrName, attrs.getValue(attrIndex));
+                }
             } else {
                 mState = UNKNOWN;
             }
@@ -275,7 +281,13 @@
             set.add(word);
         }
         // TODO: use an XMLSerializer if this gets big
-        destination.write("<wordlist format=\"2\">\n");
+        destination.write("<wordlist format=\"2\"");
+        final HashMap<String, String> options = dict.mOptions.mAttributes;
+        for (final String key : dict.mOptions.mAttributes.keySet()) {
+            final String value = dict.mOptions.mAttributes.get(key);
+            destination.write(" " + key + "=\"" + value + "\"");
+        }
+        destination.write(">\n");
         destination.write("<!-- Warning: there is no code to read this format yet. -->\n");
         for (Word word : set) {
             destination.write("  <" + WORD_TAG + " " + WORD_ATTR + "=\"" + word.mWord + "\" "