Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 723a27ef authored by satok's avatar satok
Browse files

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
parent a6689ddb
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -2395,6 +2395,14 @@ public final class Settings {
        public static final String SELECTED_INPUT_METHOD_SUBTYPE =
                "selected_input_method_subtype";

        /**
         * Setting to record the history of input method subtype, holding the pair of ID of IME
         * and its last used subtype.
         * @hide
         */
        public static final String INPUT_METHODS_SUBTYPE_HISTORY =
                "input_methods_subtype_history";

        /**
         * Whether the device has been provisioned (0 = false, 1 = true)
         */
+228 −47
Original line number Diff line number Diff line
@@ -116,6 +116,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
    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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
                                    if (!doit) {
                                        return true;
                                    }
                                    Settings.Secure.putString(mContext.getContentResolver(),
                                            Settings.Secure.DEFAULT_INPUT_METHOD, "");
                                    resetSelectedInputMethodSubtype();
                                    resetSelectedInputMethodAndSubtypeLocked("");
                                    chooseNewDefaultIMELocked();
                                    return true;
                                }
@@ -425,9 +424,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
                                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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
                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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
                    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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub

        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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
                }
            }
            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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
                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
                    resetSelectedInputMethodAndSubtypeLocked(enabledInputMethodsList.size() > 0
                            ? enabledInputMethodsList.get(0).first : "");
                    resetSelectedInputMethodSubtype();
                }
                // Previous state was enabled.
                return true;
@@ -1809,30 +1799,63 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
        }
    }

    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) {
    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 >= 0 && subtypeId < subtypes.size()) {
            Settings.Secure.putInt(mContext.getContentResolver(),
                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
                    subtypes.get(subtypeId).hashCode());
            if (subtypeId < subtypes.size()) {
                mSettings.putSelectedSubtype(subtypes.get(subtypeId).hashCode());
                mCurrentSubtype = subtypes.get(subtypeId);
            } else {
            resetSelectedInputMethodSubtype();
                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) {
        InputMethodInfo imi = mMethodMap.get(id);
        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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
        } 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
        // 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
            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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
        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);
        }
    }

    // ----------------------------------------------------------------------