Merge "Revert all hdpi assets to gingerbread"
diff --git a/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java b/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java
index b4fe327..c926be0 100644
--- a/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.compat;
 
+import com.android.inputmethod.latin.EditingUtils.SelectedWord;
+
 import android.util.Log;
 import android.view.inputmethod.InputConnection;
 
@@ -33,6 +35,16 @@
             .getConstructor(CLASS_CorrectionInfo, INPUT_TYPE_CorrectionInfo);
     private static final Method METHOD_InputConnection_commitCorrection = CompatUtils
             .getMethod(InputConnection.class, "commitCorrection", CLASS_CorrectionInfo);
+    private static final Method METHOD_getSelectedText = CompatUtils
+            .getMethod(InputConnection.class, "getSelectedText", int.class);
+    private static final Method METHOD_setComposingRegion = CompatUtils
+            .getMethod(InputConnection.class, "setComposingRegion", int.class, int.class);
+    public static final boolean RECORRECTION_SUPPORTED;
+
+    static {
+        RECORRECTION_SUPPORTED = METHOD_getSelectedText != null
+                && METHOD_setComposingRegion != null;
+    }
 
     public static void commitCorrection(InputConnection ic, int offset, CharSequence oldText,
             CharSequence newText) {
@@ -55,4 +67,25 @@
             Log.e(TAG, "Error in commitCorrection: InvocationTargetException");
         }
     }
+
+
+    /**
+     * Returns the selected text between the selStart and selEnd positions.
+     */
+    public static CharSequence getSelectedText(InputConnection ic, int selStart, int selEnd) {
+        // Use reflection, for backward compatibility
+        return (CharSequence) CompatUtils.invoke(
+                ic, null, METHOD_getSelectedText, 0);
+    }
+
+    /**
+     * Tries to set the text into composition mode if there is support for it in the framework.
+     */
+    public static void underlineWord(InputConnection ic, SelectedWord word) {
+        // Use reflection, for backward compatibility
+        // If method not found, there's nothing we can do. It still works but just wont underline
+        // the word.
+        CompatUtils.invoke(
+                ic, null, METHOD_setComposingRegion, word.mStart, word.mEnd);
+    }
 }
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
index 5e66bf4..80586b7 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
@@ -16,8 +16,13 @@
 
 package com.android.inputmethod.compat;
 
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.Utils;
+
 import android.content.Context;
 import android.os.IBinder;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
@@ -27,6 +32,7 @@
 import java.util.Collections;
 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
@@ -50,7 +56,15 @@
     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.
+    private static final boolean ALLOW_DUMMY_SUBTYPE = true;
+    private static final boolean HAS_VOICE_FUNCTION = true;
+    private static final String VOICE_MODE = "voice";
+    private static final String KEYBOARD_MODE = "keyboard";
+
     private InputMethodManager mImm;
+    private String mLatinImePackageName;
     private InputMethodManagerCompatWrapper() {
     }
 
@@ -64,28 +78,82 @@
     private synchronized void init(Context context) {
         mImm = (InputMethodManager) context.getSystemService(
                 Context.INPUT_METHOD_SERVICE);
+        if (context instanceof LatinIME) {
+            mLatinImePackageName = context.getPackageName();
+        }
     }
 
     public InputMethodSubtypeCompatWrapper getCurrentInputMethodSubtype() {
-        return new InputMethodSubtypeCompatWrapper(
-                CompatUtils.invoke(mImm, null, METHOD_getCurrentInputMethodSubtype));
+        Object o = CompatUtils.invoke(mImm, null, METHOD_getCurrentInputMethodSubtype);
+        return new InputMethodSubtypeCompatWrapper(o);
     }
 
     public List<InputMethodSubtypeCompatWrapper> getEnabledInputMethodSubtypeList(
             InputMethodInfoCompatWrapper imi, boolean allowsImplicitlySelectedSubtypes) {
         Object retval = CompatUtils.invoke(mImm, null, METHOD_getEnabledInputMethodSubtypeList,
                 (imi != null ? imi.getInputMethodInfo() : null), allowsImplicitlySelectedSubtypes);
-        // Returns an empty list
-        if (retval == null)
-            return Collections.emptyList();
+        if (retval == null || !(retval instanceof List) || ((List<?>)retval).isEmpty()) {
+            if (!ALLOW_DUMMY_SUBTYPE) {
+                // Returns an empty list
+                return Collections.emptyList();
+            }
+            // Creates dummy subtypes
+            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((List<?>)retval);
     }
 
+    private InputMethodInfoCompatWrapper getLatinImeInputMethodInfo() {
+        if (TextUtils.isEmpty(mLatinImePackageName))
+            return null;
+        return Utils.getInputMethodInfo(this, mLatinImePackageName);
+    }
+
+    @SuppressWarnings("unused")
+    private InputMethodSubtypeCompatWrapper getLastResortSubtype(String mode) {
+        if (VOICE_MODE.equals(mode) && !HAS_VOICE_FUNCTION)
+            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);
-        // Returns an empty map
-        if (!(retval instanceof Map)) return Collections.emptyMap();
+        if (retval == null || !(retval instanceof Map) || ((Map<?, ?>)retval).isEmpty()) {
+            if (!ALLOW_DUMMY_SUBTYPE) {
+                // Returns an empty map
+                return Collections.emptyMap();
+            }
+            // Creates dummy subtypes
+            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;
@@ -107,6 +175,9 @@
     }
 
     public boolean switchToLastInputMethod(IBinder token) {
+        if (SubtypeSwitcher.getInstance().isDummyVoiceMode()) {
+            return true;
+        }
         return (Boolean)CompatUtils.invoke(mImm, false, METHOD_switchToLastInputMethod, token);
     }
 
diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
index 3ffa819..317b022 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
@@ -22,6 +22,7 @@
 import android.util.Log;
 
 import java.lang.reflect.Method;
+import java.util.Arrays;
 
 // TODO: Override this class with the concrete implementation if we need to take care of the
 // performance.
@@ -48,35 +49,65 @@
     private static final Method METHOD_getExtraValueOf =
             CompatUtils.getMethod(CLASS_InputMethodSubtype, "getExtraValueOf", String.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 : null);
         if (DBG) {
             Log.d(TAG, "CreateInputMethodSubtypeCompatWrapper");
         }
+        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(null);
+        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);
         if (TextUtils.isEmpty(s)) return DEFAULT_LOCALE;
         return s;
     }
 
     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);
     }
 
@@ -92,10 +123,32 @@
     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/deprecated/VoiceProxy.java b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
index 5fba29d..0d0591b 100644
--- a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
+++ b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
@@ -82,6 +82,8 @@
     private static final String PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE =
             "has_used_voice_input_unsupported_locale";
     private static final int RECOGNITIONVIEW_HEIGHT_THRESHOLD_RATIO = 6;
+    // TODO: Adjusted on phones for now
+    private static final int RECOGNITIONVIEW_MINIMUM_HEIGHT_DIP = 244;
 
     private static final String TAG = VoiceProxy.class.getSimpleName();
     private static final boolean DEBUG = LatinImeLogger.sDBG;
@@ -99,6 +101,7 @@
     private boolean mVoiceButtonOnPrimary;
     private boolean mVoiceInputHighlighted;
 
+    private int mMinimumVoiceRecognitionViewHeightPixel;
     private InputMethodManagerCompatWrapper mImm;
     private LatinIME mService;
     private AlertDialog mVoiceWarningDialog;
@@ -107,6 +110,7 @@
     private Hints mHints;
     private UIHandler mHandler;
     private SubtypeSwitcher mSubtypeSwitcher;
+
     // For each word, a list of potential replacements, usually from voice.
     private final Map<String, List<CharSequence>> mWordToSuggestions =
             new HashMap<String, List<CharSequence>>();
@@ -123,6 +127,8 @@
     private void initInternal(LatinIME service, SharedPreferences prefs, UIHandler h) {
         mService = service;
         mHandler = h;
+        mMinimumVoiceRecognitionViewHeightPixel = Utils.dipToPixel(
+                Utils.getDipScale(service), RECOGNITIONVIEW_MINIMUM_HEIGHT_DIP);
         mImm = InputMethodManagerCompatWrapper.getInstance(service);
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         if (VOICE_INSTALLED) {
@@ -542,7 +548,11 @@
                             mService.getResources().getDisplayMetrics().heightPixels;
                     final int currentHeight = popupLayout.getLayoutParams().height;
                     final int keyboardHeight = keyboardView.getHeight();
-                    if (keyboardHeight > currentHeight || keyboardHeight
+                    if (mMinimumVoiceRecognitionViewHeightPixel > keyboardHeight
+                            || mMinimumVoiceRecognitionViewHeightPixel > currentHeight) {
+                        popupLayout.getLayoutParams().height =
+                            mMinimumVoiceRecognitionViewHeightPixel;
+                    } else if (keyboardHeight > currentHeight || keyboardHeight
                             > (displayHeight / RECOGNITIONVIEW_HEIGHT_THRESHOLD_RATIO)) {
                         popupLayout.getLayoutParams().height = keyboardHeight;
                     }
diff --git a/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java b/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java
index 52c73ce..b57c16f 100644
--- a/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java
+++ b/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java
@@ -50,7 +50,6 @@
  * plays beeps, shows errors, etc.
  */
 public class RecognitionView {
-    @SuppressWarnings("unused")
     private static final String TAG = "RecognitionView";
 
     private Handler mUiHandler;  // Reference to UI thread
diff --git a/java/src/com/android/inputmethod/latin/EditingUtils.java b/java/src/com/android/inputmethod/latin/EditingUtils.java
index 0ca06dd..80830c0 100644
--- a/java/src/com/android/inputmethod/latin/EditingUtils.java
+++ b/java/src/com/android/inputmethod/latin/EditingUtils.java
@@ -16,13 +16,13 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.compat.InputConnectionCompatUtils;
+
 import android.text.TextUtils;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.util.regex.Pattern;
 
 /**
@@ -34,11 +34,6 @@
      */
     private static final int LOOKBACK_CHARACTER_NUM = 15;
 
-    // Cache Method pointers
-    private static boolean sMethodsInitialized;
-    private static Method sMethodGetSelectedText;
-    private static Method sMethodSetComposingRegion;
-
     private EditingUtils() {
         // Unintentional empty constructor for singleton.
     }
@@ -241,7 +236,8 @@
             }
 
             // Extract the selection alone
-            CharSequence touching = getSelectedText(ic, selStart, selEnd);
+            CharSequence touching = InputConnectionCompatUtils.getSelectedText(
+                    ic, selStart, selEnd);
             if (TextUtils.isEmpty(touching)) return null;
             // Is any part of the selection a separator? If so, return null.
             final int length = touching.length();
@@ -255,74 +251,4 @@
         }
         return null;
     }
-
-    /**
-     * Cache method pointers for performance
-     */
-    private static void initializeMethodsForReflection() {
-        try {
-            // These will either both exist or not, so no need for separate try/catch blocks.
-            // If other methods are added later, use separate try/catch blocks.
-            sMethodGetSelectedText = InputConnection.class.getMethod("getSelectedText", int.class);
-            sMethodSetComposingRegion = InputConnection.class.getMethod("setComposingRegion",
-                    int.class, int.class);
-        } catch (NoSuchMethodException exc) {
-            // Ignore
-        }
-        sMethodsInitialized = true;
-    }
-
-    /**
-     * Returns the selected text between the selStart and selEnd positions.
-     */
-    private static CharSequence getSelectedText(InputConnection ic, int selStart, int selEnd) {
-        // Use reflection, for backward compatibility
-        CharSequence result = null;
-        if (!sMethodsInitialized) {
-            initializeMethodsForReflection();
-        }
-        if (sMethodGetSelectedText != null) {
-            try {
-                result = (CharSequence) sMethodGetSelectedText.invoke(ic, 0);
-                return result;
-            } catch (InvocationTargetException exc) {
-                // Ignore
-            } catch (IllegalArgumentException e) {
-                // Ignore
-            } catch (IllegalAccessException e) {
-                // Ignore
-            }
-        }
-        // Reflection didn't work, try it the poor way, by moving the cursor to the start,
-        // getting the text after the cursor and moving the text back to selected mode.
-        // TODO: Verify that this works properly in conjunction with 
-        // LatinIME#onUpdateSelection
-        ic.setSelection(selStart, selEnd);
-        result = ic.getTextAfterCursor(selEnd - selStart, 0);
-        ic.setSelection(selStart, selEnd);
-        return result;
-    }
-
-    /**
-     * Tries to set the text into composition mode if there is support for it in the framework.
-     */
-    public static void underlineWord(InputConnection ic, SelectedWord word) {
-        // Use reflection, for backward compatibility
-        // If method not found, there's nothing we can do. It still works but just wont underline
-        // the word.
-        if (!sMethodsInitialized) {
-            initializeMethodsForReflection();
-        }
-        if (sMethodSetComposingRegion != null) {
-            try {
-                sMethodSetComposingRegion.invoke(ic, word.mStart, word.mEnd);
-            } catch (InvocationTargetException exc) {
-                // Ignore
-            } catch (IllegalArgumentException e) {
-                // Ignore
-            } catch (IllegalAccessException e) {
-                // Ignore
-            }
-        }
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index ae23154..35f4312 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1771,6 +1771,7 @@
     }
 
     private void setOldSuggestions() {
+        if (!InputConnectionCompatUtils.RECORRECTION_SUPPORTED) return;
         mVoiceProxy.setShowingVoiceSuggestions(false);
         if (mCandidateView != null && mCandidateView.isShowingAddToDictionaryHint()) {
             return;
@@ -1790,7 +1791,7 @@
                     abortRecorrection(true);
                 } else {
                     TextEntryState.selectedForRecorrection();
-                    EditingUtils.underlineWord(ic, touching);
+                    InputConnectionCompatUtils.underlineWord(ic, touching);
                 }
 
                 ic.endBatchEdit();
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 10a3369..053e2ab 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -210,7 +210,7 @@
         final String newLocale;
         final String newMode;
         final String oldMode = getCurrentSubtypeMode();
-        if (newSubtype == null || !newSubtype.hasOriginalObject()) {
+        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.");
@@ -539,6 +539,11 @@
         return null == mCurrentSubtype ? false : VOICE_MODE.equals(getCurrentSubtypeMode());
     }
 
+    public boolean isDummyVoiceMode() {
+        return mCurrentSubtype != null && mCurrentSubtype.getOriginalObject() == null
+                && VOICE_MODE.equals(getCurrentSubtypeMode());
+    }
+
     private void triggerVoiceIME() {
         if (!mService.isInputViewShown()) return;
         VoiceProxy.getInstance().startListening(false,
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 35b2b12..3e092d9 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -21,6 +21,7 @@
 import com.android.inputmethod.compat.InputTypeCompatUtils;
 import com.android.inputmethod.keyboard.KeyboardId;
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.inputmethodservice.InputMethodService;
 import android.os.AsyncTask;
@@ -110,9 +111,14 @@
     }
 
     public static String getInputMethodId(InputMethodManagerCompatWrapper imm, String packageName) {
+        return getInputMethodInfo(imm, packageName).getId();
+    }
+
+    public static InputMethodInfoCompatWrapper getInputMethodInfo(
+            InputMethodManagerCompatWrapper imm, String packageName) {
         for (final InputMethodInfoCompatWrapper imi : imm.getEnabledInputMethodList()) {
             if (imi.getPackageName().equals(packageName))
-                return imi.getId();
+                return imi;
         }
         throw new RuntimeException("Can not find input method id for " + packageName);
     }
@@ -601,4 +607,14 @@
         }
         return true;
     }
+
+    public static float getDipScale(Context context) {
+        final float scale = context.getResources().getDisplayMetrics().density;
+        return scale;
+    }
+
+    /** Convert pixel to DIP */
+    public static int dipToPixel(float scale, int dip) {
+        return (int) ((float) dip * scale + 0.5);
+    }
 }