Store the current IME's token in the system bar for changing the current IME to a shortcut IME from the system bar

Bug: 3212206
Bug: 3201828

- Added a shortcut IME button. This will be used for calling a shortcut IME (e.g. Voice input)
- Made the positions of IME buttons left aligned
- IME token is required to change IME because of the security reasons.

Change-Id: I48ba5e2509b3aa1bfd2394f9201427fa6b93c6d3
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 723432d..c574058 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -964,7 +964,7 @@
             }
 
             synchronized (mMethodMap) {
-                mStatusBar.setIMEButtonVisible(visible);
+                mStatusBar.setIMEButtonVisible(token, visible);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -1007,23 +1007,29 @@
 
         if (id.equals(mCurMethodId)) {
             ArrayList<InputMethodSubtype> subtypes = info.getSubtypes();
+            InputMethodSubtype subtype = null;
             if (subtypeId >= 0 && subtypeId < subtypes.size()) {
-                InputMethodSubtype subtype = subtypes.get(subtypeId);
-                if (subtype != mCurrentSubtype) {
-                    synchronized (mMethodMap) {
-                        if (mCurMethod != null) {
-                            try {
-                                setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
-                                if (mInputShown) {
-                                    // If mInputShown is false, there is no IME button on the
-                                    // system bar.
-                                    // Thus there is no need to make it invisible explicitly.
-                                    mStatusBar.setIMEButtonVisible(true);
-                                }
-                                mCurMethod.changeInputMethodSubtype(subtype);
-                            } catch (RemoteException e) {
-                                return;
+                subtype = subtypes.get(subtypeId);
+            }
+            if (subtype != mCurrentSubtype) {
+                synchronized (mMethodMap) {
+                    if (mCurMethod != null) {
+                        try {
+                            setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
+                            if (mInputShown) {
+                                // If mInputShown is false, there is no IME button on the
+                                // system bar.
+                                // Thus there is no need to make it invisible explicitly.
+                                mStatusBar.setIMEButtonVisible(mCurToken, true);
                             }
+                            // If subtype is null, try to find the most applicable one from
+                            // getCurrentInputMethodSubtype.
+                            if (subtype == null) {
+                                subtype = getCurrentInputMethodSubtype();
+                            }
+                            mCurMethod.changeInputMethodSubtype(subtype);
+                        } catch (RemoteException e) {
+                            return;
                         }
                     }
                 }
@@ -1942,28 +1948,29 @@
      * return defaultSubtypeId
      * @return the most applicable subtypeId
      */
-    private int findLastResortApplicableSubtypeLocked(
+    private InputMethodSubtype findLastResortApplicableSubtypeLocked(
             List<InputMethodSubtype> subtypes, String mode, String locale, int defaultSubtypeId) {
         if (subtypes == null || subtypes.size() == 0) {
-            return NOT_A_SUBTYPE_ID;
+            return null;
         }
         if (TextUtils.isEmpty(locale)) {
             locale = mContext.getResources().getConfiguration().locale.toString();
         }
         final String language = locale.substring(0, 2);
         boolean partialMatchFound = false;
-        int applicableSubtypeId = defaultSubtypeId;
+        InputMethodSubtype applicableSubtype = null;
         for (int i = 0; i < subtypes.size(); ++i) {
-            final String subtypeLocale = subtypes.get(i).getLocale();
+            InputMethodSubtype subtype = subtypes.get(i);
+            final String subtypeLocale = subtype.getLocale();
             // An applicable subtype should match "mode".
             if (subtypes.get(i).getMode().equalsIgnoreCase(mode)) {
                 if (locale.equals(subtypeLocale)) {
                     // Exact match (e.g. system locale is "en_US" and subtype locale is "en_US")
-                    applicableSubtypeId = i;
+                    applicableSubtype = subtype;
                     break;
                 } else if (!partialMatchFound && subtypeLocale.startsWith(language)) {
                     // Partial match (e.g. system locale is "en_US" and subtype locale is "en")
-                    applicableSubtypeId = i;
+                    applicableSubtype = subtype;
                     partialMatchFound = true;
                 }
             }
@@ -1972,10 +1979,10 @@
         // The first subtype applicable to the system locale will be defined as the most applicable
         // subtype.
         if (DEBUG) {
-            Slog.d(TAG, "Applicable InputMethodSubtype was found: " + applicableSubtypeId + ","
-                    + subtypes.get(applicableSubtypeId).getLocale());
+            Slog.d(TAG, "Applicable InputMethodSubtype was found: " + applicableSubtype.getMode()
+                    + "," + applicableSubtype.getLocale());
         }
-        return applicableSubtypeId;
+        return applicableSubtype;
     }
 
     // If there are no selected shortcuts, tries finding the most applicable ones.
@@ -1983,59 +1990,66 @@
             findLastResortApplicableShortcutInputMethodAndSubtypeLocked(String mode) {
         List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
         InputMethodInfo mostApplicableIMI = null;
-        int mostApplicableSubtypeId = NOT_A_SUBTYPE_ID;
+        InputMethodSubtype mostApplicableSubtype = null;
         boolean foundInSystemIME = false;
 
         // Search applicable subtype for each InputMethodInfo
         for (InputMethodInfo imi: imis) {
-            int subtypeId = NOT_A_SUBTYPE_ID;
+            InputMethodSubtype subtype = null;
             if (mCurrentSubtype != null) {
                 // 1. Search with the current subtype's locale and the enabled subtypes
-                subtypeId = findLastResortApplicableSubtypeLocked(
+                subtype = findLastResortApplicableSubtypeLocked(
                         mSettings.getEnabledInputMethodSubtypeListLocked(
                         imi), mode, mCurrentSubtype.getLocale(), NOT_A_SUBTYPE_ID);
-                if (subtypeId == NOT_A_SUBTYPE_ID) {
+                if (subtype == null) {
                     // 2. Search with the current subtype's locale and all subtypes
-                    subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
+                    subtype = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
                             mode, mCurrentSubtype.getLocale(), NOT_A_SUBTYPE_ID);
                 }
             }
             // 3. Search with the system locale and the enabled subtypes
-            if (subtypeId == NOT_A_SUBTYPE_ID) {
-                subtypeId = findLastResortApplicableSubtypeLocked(
+            if (subtype == null) {
+                subtype = findLastResortApplicableSubtypeLocked(
                         mSettings.getEnabledInputMethodSubtypeListLocked(
                         imi), mode, null, NOT_A_SUBTYPE_ID);
             }
-            if (subtypeId == NOT_A_SUBTYPE_ID) {
+            if (subtype == null) {
                 // 4. Search with the system locale and all subtypes
-                subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
+                subtype = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
                         mode, null, NOT_A_SUBTYPE_ID);
             }
-            if (subtypeId != NOT_A_SUBTYPE_ID) {
+            if (subtype != null) {
                 if (imi.getId().equals(mCurMethodId)) {
                     // The current input method is the most applicable IME.
                     mostApplicableIMI = imi;
-                    mostApplicableSubtypeId = subtypeId;
+                    mostApplicableSubtype = subtype;
                     break;
                 } else if ((imi.getServiceInfo().applicationInfo.flags
                         & ApplicationInfo.FLAG_SYSTEM) != 0) {
                     // The system input method is 2nd applicable IME.
                     mostApplicableIMI = imi;
-                    mostApplicableSubtypeId = subtypeId;
+                    mostApplicableSubtype = subtype;
                     foundInSystemIME = true;
                 } else if (!foundInSystemIME) {
                     mostApplicableIMI = imi;
-                    mostApplicableSubtypeId = subtypeId;
+                    mostApplicableSubtype = subtype;
                 }
             }
         }
         if (DEBUG) {
-            Slog.w(TAG, "Most applicable shortcut input method subtype was:"
-                    + mostApplicableIMI.getId() + "," + mostApplicableSubtypeId);
+            if (mostApplicableIMI != null) {
+                Slog.w(TAG, "Most applicable shortcut input method was:"
+                        + mostApplicableIMI.getId());
+                if (mostApplicableSubtype != null) {
+                    Slog.w(TAG, "Most applicable shortcut input method subtype was:"
+                            + "," + mostApplicableSubtype.getMode() + ","
+                            + mostApplicableSubtype.getLocale());
+                }
+            }
         }
-        if (mostApplicableIMI != null && mostApplicableSubtypeId != NOT_A_SUBTYPE_ID) {
+        if (mostApplicableIMI != null) {
             return new Pair<InputMethodInfo, InputMethodSubtype> (mostApplicableIMI,
-                    mostApplicableIMI.getSubtypes().get(mostApplicableSubtypeId));
+                    mostApplicableSubtype);
         } else {
             return null;
         }
@@ -2063,15 +2077,12 @@
                         // the most applicable subtype from all subtypes whose mode is
                         // SUBTYPE_MODE_KEYBOARD. This is an exceptional case, so we will hardcode
                         // the mode.
-                        subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
+                        mCurrentSubtype = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
                                 SUBTYPE_MODE_KEYBOARD, null, DEFAULT_SUBTYPE_ID);
                     }
-                }
-                if (subtypeId != NOT_A_SUBTYPE_ID) {
+                } else {
                     mCurrentSubtype =
                             mMethodMap.get(lastInputMethodId).getSubtypes().get(subtypeId);
-                } else {
-                    mCurrentSubtype = null;
                 }
             }
             return mCurrentSubtype;
@@ -2101,7 +2112,7 @@
                             SUBTYPE_MODE_VOICE);
                 addShortcutInputMethodAndSubtypes(info.first, info.second);
             }
-            ArrayList ret = new ArrayList<Object>();
+            ArrayList<Object> ret = new ArrayList<Object>();
             for (InputMethodInfo imi: mShortcutInputMethodsAndSubtypes.keySet()) {
                 ret.add(imi);
                 for (InputMethodSubtype subtype: mShortcutInputMethodsAndSubtypes.get(imi)) {