Save the history of InputMethodSubtype for reverting the subtype when IME was changed.

- Re-revert the change https://android-git.corp.google.com/g/#q,78962,n,z
*** If you want to see the diff between reverted change and new change, please see the diff between patch set 1 and the latest patch set

- TODO: Add a public function "switchToLastInputMethod"

Change-Id: Ic85d54b3b68e47a22360acaeb81202a366a34586
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index d035eb5..0d3cfde 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -116,6 +116,7 @@
     static final long TIME_TO_RECONNECT = 10*1000;
 
     private static final int NOT_A_SUBTYPE_ID = -1;
+    private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID);
     // If IME doesn't support the system locale, the default subtype will be the first defined one.
     private static final int DEFAULT_SUBTYPE_ID = 0;
 
@@ -366,9 +367,7 @@
                                     if (!doit) {
                                         return true;
                                     }
-                                    Settings.Secure.putString(mContext.getContentResolver(),
-                                            Settings.Secure.DEFAULT_INPUT_METHOD, "");
-                                    resetSelectedInputMethodSubtype();
+                                    resetSelectedInputMethodAndSubtypeLocked("");
                                     chooseNewDefaultIMELocked();
                                     return true;
                                 }
@@ -425,9 +424,7 @@
                                 changed = true;
                                 curIm = null;
                                 Slog.i(TAG, "Unsetting current input method");
-                                Settings.Secure.putString(mContext.getContentResolver(),
-                                        Settings.Secure.DEFAULT_INPUT_METHOD, "");
-                                resetSelectedInputMethodSubtype();
+                                resetSelectedInputMethodAndSubtypeLocked("");
                             }
                         }
                     }
@@ -510,9 +507,7 @@
                 Slog.i(TAG, "No default found, using " + defIm.getId());
             }
             if (defIm != null) {
-                Settings.Secure.putString(mContext.getContentResolver(),
-                        Settings.Secure.DEFAULT_INPUT_METHOD, defIm.getId());
-                putSelectedInputMethodSubtype(defIm, NOT_A_SUBTYPE_ID);
+                setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
             }
         }
 
@@ -994,7 +989,7 @@
                     synchronized (mMethodMap) {
                         if (mCurMethod != null) {
                             try {
-                                putSelectedInputMethodSubtype(info, subtypeId);
+                                setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
                                 if (mInputShown) {
                                     // If mInputShown is false, there is no IME button on the
                                     // system bar.
@@ -1014,13 +1009,13 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            mCurMethodId = id;
             // Set a subtype to this input method.
             // subtypeId the name of a subtype which will be set.
-            putSelectedInputMethodSubtype(info, subtypeId);
-
-            Settings.Secure.putString(mContext.getContentResolver(),
-                    Settings.Secure.DEFAULT_INPUT_METHOD, id);
+            setSelectedInputMethodAndSubtypeLocked(info, subtypeId, false);
+            // mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked()
+            // because mCurMethodId is stored as a history in
+            // setSelectedInputMethodAndSubtypeLocked().
+            mCurMethodId = id;
 
             if (ActivityManagerNative.isSystemReady()) {
                 Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
@@ -1487,9 +1482,7 @@
                 }
             }
             InputMethodInfo imi = enabled.get(i);
-            Settings.Secure.putString(mContext.getContentResolver(),
-                    Settings.Secure.DEFAULT_INPUT_METHOD, imi.getId());
-            putSelectedInputMethodSubtype(imi, NOT_A_SUBTYPE_ID);
+            resetSelectedInputMethodAndSubtypeLocked(imi.getId());
             return true;
         }
 
@@ -1793,11 +1786,8 @@
                 String selId = Settings.Secure.getString(mContext.getContentResolver(),
                         Settings.Secure.DEFAULT_INPUT_METHOD);
                 if (id.equals(selId)) {
-                    Settings.Secure.putString(
-                            mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD,
-                            enabledInputMethodsList.size() > 0
-                                    ? enabledInputMethodsList.get(0).first : "");
-                    resetSelectedInputMethodSubtype();
+                    resetSelectedInputMethodAndSubtypeLocked(enabledInputMethodsList.size() > 0
+                            ? enabledInputMethodsList.get(0).first : "");
                 }
                 // Previous state was enabled.
                 return true;
@@ -1809,22 +1799,56 @@
         }
     }
 
-    private void resetSelectedInputMethodSubtype() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, NOT_A_SUBTYPE_ID);
+    private void saveCurrentInputMethodAndSubtypeToHistory() {
+        String subtypeId = NOT_A_SUBTYPE_ID_STR;
+        if (mCurrentSubtype != null) {
+            subtypeId = String.valueOf(mCurrentSubtype.hashCode());
+        }
+        mSettings.addSubtypeToHistory(mCurMethodId, subtypeId);
     }
 
-    private void putSelectedInputMethodSubtype(InputMethodInfo imi, int subtypeId) {
-        final ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
-        if (subtypeId >= 0 && subtypeId < subtypes.size()) {
-            Settings.Secure.putInt(mContext.getContentResolver(),
-                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
-                    subtypes.get(subtypeId).hashCode());
-            mCurrentSubtype = subtypes.get(subtypeId);
-        } else {
-            resetSelectedInputMethodSubtype();
+    private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
+            boolean setSubtypeOnly) {
+        // Update the history of InputMethod and Subtype
+        saveCurrentInputMethodAndSubtypeToHistory();
+
+        // Set Subtype here
+        if (imi == null || subtypeId < 0) {
+            mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
             mCurrentSubtype = null;
+        } else {
+            final ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+            if (subtypeId < subtypes.size()) {
+                mSettings.putSelectedSubtype(subtypes.get(subtypeId).hashCode());
+                mCurrentSubtype = subtypes.get(subtypeId);
+            } else {
+                mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+                mCurrentSubtype = null;
+            }
         }
+
+        if (!setSubtypeOnly) {
+            // Set InputMethod here
+            mSettings.putSelectedInputMethod(imi != null ? imi.getId() : "");
+        }
+    }
+
+    private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
+        InputMethodInfo imi = mMethodMap.get(newDefaultIme);
+        int lastSubtypeId = NOT_A_SUBTYPE_ID;
+        // newDefaultIme is empty when there is no candidate for the selected IME.
+        if (imi != null && !TextUtils.isEmpty(newDefaultIme)) {
+            String subtypeHashCode = mSettings.getLastSubtypeForInputMethodLocked(newDefaultIme);
+            if (subtypeHashCode != null) {
+                try {
+                    lastSubtypeId = getSubtypeIdFromHashCode(
+                            imi, Integer.valueOf(subtypeHashCode));
+                } catch (NumberFormatException e) {
+                    Slog.w(TAG, "HashCode for subtype looks broken: " + subtypeHashCode, e);
+                }
+            }
+        }
+        setSelectedInputMethodAndSubtypeLocked(imi, lastSubtypeId, false);
     }
 
     private int getSelectedInputMethodSubtypeId(String id) {
@@ -1832,7 +1856,6 @@
         if (imi == null) {
             return NOT_A_SUBTYPE_ID;
         }
-        ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
         int subtypeId;
         try {
             subtypeId = Settings.Secure.getInt(mContext.getContentResolver(),
@@ -1840,9 +1863,14 @@
         } catch (SettingNotFoundException e) {
             return NOT_A_SUBTYPE_ID;
         }
+        return getSubtypeIdFromHashCode(imi, subtypeId);
+    }
+
+    private int getSubtypeIdFromHashCode(InputMethodInfo imi, int subtypeHashCode) {
+        ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
         for (int i = 0; i < subtypes.size(); ++i) {
             InputMethodSubtype ims = subtypes.get(i);
-            if (subtypeId == ims.hashCode()) {
+            if (subtypeHashCode == ims.hashCode()) {
                 return i;
             }
         }
@@ -1922,10 +1950,10 @@
         // example: ("ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0")
         private static final char INPUT_METHOD_SEPARATER = ':';
         private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';';
-        private final TextUtils.SimpleStringSplitter mStringColonSplitter =
+        private final TextUtils.SimpleStringSplitter mInputMethodSplitter =
                 new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATER);
 
-        private final TextUtils.SimpleStringSplitter mStringSemiColonSplitter =
+        private final TextUtils.SimpleStringSplitter mSubtypeSplitter =
                 new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATER);
 
         private final ContentResolver mResolver;
@@ -1988,16 +2016,16 @@
             if (TextUtils.isEmpty(enabledInputMethodsStr)) {
                 return imsList;
             }
-            mStringColonSplitter.setString(enabledInputMethodsStr);
-            while (mStringColonSplitter.hasNext()) {
-                String nextImsStr = mStringColonSplitter.next();
-                mStringSemiColonSplitter.setString(nextImsStr);
-                if (mStringSemiColonSplitter.hasNext()) {
+            mInputMethodSplitter.setString(enabledInputMethodsStr);
+            while (mInputMethodSplitter.hasNext()) {
+                String nextImsStr = mInputMethodSplitter.next();
+                mSubtypeSplitter.setString(nextImsStr);
+                if (mSubtypeSplitter.hasNext()) {
                     ArrayList<String> subtypeHashes = new ArrayList<String>();
                     // The first element is ime id.
-                    String imeId = mStringSemiColonSplitter.next();
-                    while (mStringSemiColonSplitter.hasNext()) {
-                        subtypeHashes.add(mStringSemiColonSplitter.next());
+                    String imeId = mSubtypeSplitter.next();
+                    while (mSubtypeSplitter.hasNext()) {
+                        subtypeHashes.add(mSubtypeSplitter.next());
                     }
                     imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
                 }
@@ -2083,8 +2111,161 @@
         private String getEnabledInputMethodsStr() {
             mEnabledInputMethodsStrCache = Settings.Secure.getString(
                     mResolver, Settings.Secure.ENABLED_INPUT_METHODS);
+            if (DEBUG) {
+                Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache);
+            }
             return mEnabledInputMethodsStrCache;
         }
+
+        private void saveSubtypeHistory(
+                List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) {
+            StringBuilder builder = new StringBuilder();
+            boolean isImeAdded = false;
+            if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) {
+                builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATER).append(
+                        newSubtypeId);
+                isImeAdded = true;
+            }
+            for (Pair<String, String> ime: savedImes) {
+                String imeId = ime.first;
+                String subtypeId = ime.second;
+                if (TextUtils.isEmpty(subtypeId)) {
+                    subtypeId = NOT_A_SUBTYPE_ID_STR;
+                }
+                if (isImeAdded) {
+                    builder.append(INPUT_METHOD_SEPARATER);
+                } else {
+                    isImeAdded = true;
+                }
+                builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATER).append(
+                        subtypeId);
+            }
+            // Remove the last INPUT_METHOD_SEPARATER
+            putSubtypeHistoryStr(builder.toString());
+        }
+
+        public void addSubtypeToHistory(String imeId, String subtypeId) {
+            List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
+            for (Pair<String, String> ime: subtypeHistory) {
+                if (ime.first.equals(imeId)) {
+                    if (DEBUG) {
+                        Slog.v(TAG, "Subtype found in the history: " + imeId
+                                + ime.second);
+                    }
+                    // We should break here
+                    subtypeHistory.remove(ime);
+                    break;
+                }
+            }
+            saveSubtypeHistory(subtypeHistory, imeId, subtypeId);
+        }
+
+        private void putSubtypeHistoryStr(String str) {
+            if (DEBUG) {
+                Slog.d(TAG, "putSubtypeHistoryStr: " + str);
+            }
+            Settings.Secure.putString(
+                    mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, str);
+        }
+
+        public Pair<String, String> getLastInputMethodAndSubtypeLocked() {
+            // Gets the first one from the history
+            return getLastSubtypeForInputMethodLockedInternal(null);
+        }
+
+        public String getLastSubtypeForInputMethodLocked(String imeId) {
+            Pair<String, String> ime = getLastSubtypeForInputMethodLockedInternal(imeId);
+            if (ime != null) {
+                return ime.second;
+            } else {
+                return null;
+            }
+        }
+
+        private Pair<String, String> getLastSubtypeForInputMethodLockedInternal(String imeId) {
+            List<Pair<String, ArrayList<String>>> enabledImes =
+                    getEnabledInputMethodsAndSubtypeListLocked();
+            List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
+            for (Pair<String, String> imeAndSubtype: subtypeHistory) {
+                final String imeInTheHistory = imeAndSubtype.first;
+                // If imeId is empty, returns the first IME and subtype in the history
+                if (TextUtils.isEmpty(imeId) || imeInTheHistory.equals(imeId)) {
+                    final String subtypeInTheHistory = imeAndSubtype.second;
+                    final String subtypeHashCode = getEnabledSubtypeForInputMethodAndSubtypeLocked(
+                            enabledImes, imeInTheHistory, subtypeInTheHistory);
+                    if (!TextUtils.isEmpty(subtypeHashCode)) {
+                        if (DEBUG) {
+                            Slog.d(TAG, "Enabled subtype found in the history:" + subtypeHashCode);
+                        }
+                        return new Pair<String, String>(imeInTheHistory, subtypeHashCode);
+                    }
+                }
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "No enabled IME found in the history");
+            }
+            return null;
+        }
+
+        private String getEnabledSubtypeForInputMethodAndSubtypeLocked(List<Pair<String,
+                ArrayList<String>>> enabledImes, String imeId, String subtypeHashCode) {
+            for (Pair<String, ArrayList<String>> enabledIme: enabledImes) {
+                if (enabledIme.first.equals(imeId)) {
+                    for (String s: enabledIme.second) {
+                        if (s.equals(subtypeHashCode)) {
+                            // If both imeId and subtypeId are enabled, return subtypeId.
+                            return s;
+                        }
+                    }
+                    // If imeId was enabled but subtypeId was disabled.
+                    return NOT_A_SUBTYPE_ID_STR;
+                }
+            }
+            // If both imeId and subtypeId are disabled, return null
+            return null;
+        }
+
+        private List<Pair<String, String>> loadInputMethodAndSubtypeHistoryLocked() {
+            ArrayList<Pair<String, String>> imsList = new ArrayList<Pair<String, String>>();
+            final String subtypeHistoryStr = getSubtypeHistoryStr();
+            if (TextUtils.isEmpty(subtypeHistoryStr)) {
+                return imsList;
+            }
+            mInputMethodSplitter.setString(subtypeHistoryStr);
+            while (mInputMethodSplitter.hasNext()) {
+                String nextImsStr = mInputMethodSplitter.next();
+                mSubtypeSplitter.setString(nextImsStr);
+                if (mSubtypeSplitter.hasNext()) {
+                    String subtypeId = NOT_A_SUBTYPE_ID_STR;
+                    // The first element is ime id.
+                    String imeId = mSubtypeSplitter.next();
+                    while (mSubtypeSplitter.hasNext()) {
+                        subtypeId = mSubtypeSplitter.next();
+                        break;
+                    }
+                    imsList.add(new Pair<String, String>(imeId, subtypeId));
+                }
+            }
+            return imsList;
+        }
+
+        private String getSubtypeHistoryStr() {
+            if (DEBUG) {
+                Slog.d(TAG, "getSubtypeHistoryStr: " + Settings.Secure.getString(
+                        mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY));
+            }
+            return Settings.Secure.getString(
+                    mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY);
+        }
+
+        public void putSelectedInputMethod(String imeId) {
+            Settings.Secure.putString(mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, imeId);
+        }
+
+        public void putSelectedSubtype(int subtypeId) {
+            Settings.Secure.putInt(
+                    mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, subtypeId);
+        }
     }
 
     // ----------------------------------------------------------------------