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);
+ }
}
// ----------------------------------------------------------------------