Change return type of getShortcutInputMethodsAndSubtypes to Map<InputMethodInfo, List<InputMethodSubtype>>

bug: 3201828

- Brushed up the code

Change-Id: I11ad9d1d749bd8947144ca7f1676bab3cf777fd6
diff --git a/api/current.xml b/api/current.xml
index e6ac507..86e8f8b 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -221206,7 +221206,7 @@
 >
 </method>
 <method name="getShortcutInputMethodsAndSubtypes"
- return="java.util.List&lt;android.util.Pair&lt;android.view.inputmethod.InputMethodInfo, android.view.inputmethod.InputMethodSubtype&gt;&gt;"
+ return="java.util.Map&lt;android.view.inputmethod.InputMethodInfo, java.util.List&lt;android.view.inputmethod.InputMethodSubtype&gt;&gt;"
  abstract="false"
  native="false"
  synchronized="false"
@@ -249195,7 +249195,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a5f3ade..621a908 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -23,7 +23,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -49,7 +48,9 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -1454,30 +1455,29 @@
         }
     }
 
-    public List<Pair<InputMethodInfo, InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
+    public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
         synchronized (mH) {
-            List<Pair<InputMethodInfo, InputMethodSubtype>> ret =
-                    new ArrayList<Pair<InputMethodInfo, InputMethodSubtype>>();
+            HashMap<InputMethodInfo, List<InputMethodSubtype>> ret =
+                    new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
             try {
                 // TODO: We should change the return type from List<Object> to List<Parcelable>
                 List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
-                // "info" has imi1, subtype1, imi2, subtype2, imi3, subtype3,..... in the list
-                Object imi;
-                Object subtype;
-                if (info != null && info.size() > 0) {
-                    final int N = info.size();
-                    if (N % 2 == 0) {
-                        for (int i = 0; i < N;) {
-                            if ((imi = info.get(i++)) instanceof InputMethodInfo) {
-                                subtype = info.get(i++);
-                                ret.add(new Pair<InputMethodInfo, InputMethodSubtype> (
-                                        (InputMethodInfo)imi,
-                                        (subtype instanceof InputMethodSubtype) ?
-                                                (InputMethodSubtype)subtype : null));
+                // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list
+                ArrayList<InputMethodSubtype> subtypes = null;
+                final int N = info.size();
+                if (info != null && N > 0) {
+                    for (int i = 0; i < N; ++i) {
+                        Object o = info.get(i);
+                        if (o instanceof InputMethodInfo) {
+                            if (ret.containsKey(o)) {
+                                Log.e(TAG, "IMI list already contains the same InputMethod.");
+                                break;
                             }
+                            subtypes = new ArrayList<InputMethodSubtype>();
+                            ret.put((InputMethodInfo)o, subtypes);
+                        } else if (subtypes != null && o instanceof InputMethodSubtype) {
+                            subtypes.add((InputMethodSubtype)o);
                         }
-                    } else {
-                        Log.w(TAG, "The size of list was illegal.");
                     }
                 }
             } catch (RemoteException e) {
@@ -1486,6 +1486,7 @@
             return ret;
         }
     }
+
     public boolean switchToLastInputMethod(IBinder imeToken) {
         synchronized (mH) {
             try {
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index b2cc6bd..eca37b7 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -240,10 +240,9 @@
     private InputMethodSubtype mCurrentSubtype;
 
     // This list contains the pairs of InputMethodInfo and InputMethodSubtype.
-    private List<Pair<InputMethodInfo, InputMethodSubtype>> mShortcutInputMethodsAndSubtypes;
-    // This list is used for returning the pairs of InputMethodInfo and InputMethodSubtype through
-    // aidl. This list has imi1, subtype1 imi2, subtype2...
-    private List mShortcutInputMethodsAndSubtypesObjectList;
+    private final HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
+            mShortcutInputMethodsAndSubtypes =
+                new HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>();
 
     /**
      * Set to true if our ServiceConnection is currently actively bound to
@@ -992,7 +991,7 @@
                 mCurMethodId = null;
                 unbindCurrentMethodLocked(true);
             }
-            mShortcutInputMethodsAndSubtypes = null;
+            mShortcutInputMethodsAndSubtypes.clear();
         } else {
             // There is no longer an input method set, so stop any current one.
             mCurMethodId = null;
@@ -2022,7 +2021,6 @@
                     + mostApplicableIMI.getId() + "," + mostApplicableSubtypeId);
         }
         if (mostApplicableIMI != null && mostApplicableSubtypeId != NOT_A_SUBTYPE_ID) {
-            ArrayList<Parcelable> ret = new ArrayList<Parcelable>(2);
             return new Pair<InputMethodInfo, InputMethodSubtype> (mostApplicableIMI,
                     mostApplicableIMI.getSubtypes().get(mostApplicableSubtypeId));
         } else {
@@ -2067,25 +2065,37 @@
         }
     }
 
+    private void addShortcutInputMethodAndSubtypes(InputMethodInfo imi,
+            InputMethodSubtype subtype) {
+        if (mShortcutInputMethodsAndSubtypes.containsKey(imi)) {
+            mShortcutInputMethodsAndSubtypes.get(imi).add(subtype);
+        } else {
+            ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(subtype);
+            mShortcutInputMethodsAndSubtypes.put(imi, subtypes);
+        }
+    }
+
     // TODO: We should change the return type from List to List<Parcelable>
     public List getShortcutInputMethodsAndSubtypes() {
         synchronized (mMethodMap) {
-            if (mShortcutInputMethodsAndSubtypesObjectList != null) {
+            if (mShortcutInputMethodsAndSubtypes.size() == 0) {
                 // If there are no selected shortcut subtypes, the framework will try to find
                 // the most applicable subtype from all subtypes whose mode is
                 // SUBTYPE_MODE_VOICE. This is an exceptional case, so we will hardcode the mode.
-                mShortcutInputMethodsAndSubtypes =
-                        new ArrayList<Pair<InputMethodInfo, InputMethodSubtype>>();
-                mShortcutInputMethodsAndSubtypes.add(
-                        findLastResortApplicableShortcutInputMethodAndSubtypeLocked(
-                                SUBTYPE_MODE_VOICE));
-                mShortcutInputMethodsAndSubtypesObjectList = new ArrayList<Parcelable>();
-                for (Pair ime: mShortcutInputMethodsAndSubtypes) {
-                    mShortcutInputMethodsAndSubtypesObjectList.add(ime.first);
-                    mShortcutInputMethodsAndSubtypesObjectList.add(ime.second);
+                Pair<InputMethodInfo, InputMethodSubtype> info =
+                    findLastResortApplicableShortcutInputMethodAndSubtypeLocked(
+                            SUBTYPE_MODE_VOICE);
+                addShortcutInputMethodAndSubtypes(info.first, info.second);
+            }
+            ArrayList ret = new ArrayList<Object>();
+            for (InputMethodInfo imi: mShortcutInputMethodsAndSubtypes.keySet()) {
+                ret.add(imi);
+                for (InputMethodSubtype subtype: mShortcutInputMethodsAndSubtypes.get(imi)) {
+                    ret.add(subtype);
                 }
             }
-            return mShortcutInputMethodsAndSubtypesObjectList;
+            return ret;
         }
     }