diff options
4 files changed, 147 insertions, 27 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e6b39b5..de536bd 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5375,6 +5375,7 @@ public final class Settings { BACKUP_AUTO_RESTORE, ENABLED_ACCESSIBILITY_SERVICES, ENABLED_NOTIFICATION_LISTENERS, + ENABLED_INPUT_METHODS, TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, TOUCH_EXPLORATION_ENABLED, ACCESSIBILITY_ENABLED, diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java index 57fcf57..06bdb24 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -764,17 +764,55 @@ public class InputMethodUtils { private int[] mCurrentProfileIds = new int[0]; private static void buildEnabledInputMethodsSettingString( - StringBuilder builder, Pair<String, ArrayList<String>> pair) { - String id = pair.first; - ArrayList<String> subtypes = pair.second; - builder.append(id); + StringBuilder builder, Pair<String, ArrayList<String>> ime) { + builder.append(ime.first); // Inputmethod and subtypes are saved in the settings as follows: // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1 - for (String subtypeId: subtypes) { + for (String subtypeId: ime.second) { builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId); } } + public static String buildInputMethodsSettingString( + List<Pair<String, ArrayList<String>>> allImeSettingsMap) { + final StringBuilder b = new StringBuilder(); + boolean needsSeparator = false; + for (Pair<String, ArrayList<String>> ime : allImeSettingsMap) { + if (needsSeparator) { + b.append(INPUT_METHOD_SEPARATER); + } + buildEnabledInputMethodsSettingString(b, ime); + needsSeparator = true; + } + return b.toString(); + } + + public static List<Pair<String, ArrayList<String>>> buildInputMethodsAndSubtypeList( + String enabledInputMethodsStr, + TextUtils.SimpleStringSplitter inputMethodSplitter, + TextUtils.SimpleStringSplitter subtypeSplitter) { + ArrayList<Pair<String, ArrayList<String>>> imsList = + new ArrayList<Pair<String, ArrayList<String>>>(); + if (TextUtils.isEmpty(enabledInputMethodsStr)) { + return imsList; + } + inputMethodSplitter.setString(enabledInputMethodsStr); + while (inputMethodSplitter.hasNext()) { + String nextImsStr = inputMethodSplitter.next(); + subtypeSplitter.setString(nextImsStr); + if (subtypeSplitter.hasNext()) { + ArrayList<String> subtypeHashes = new ArrayList<String>(); + // The first element is ime id. + String imeId = subtypeSplitter.next(); + while (subtypeSplitter.hasNext()) { + subtypeHashes.add(subtypeSplitter.next()); + } + imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes)); + } + } + return imsList; + } + public InputMethodSettings( Resources res, ContentResolver resolver, HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList, @@ -875,27 +913,9 @@ public class InputMethodUtils { } public List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() { - ArrayList<Pair<String, ArrayList<String>>> imsList - = new ArrayList<Pair<String, ArrayList<String>>>(); - final String enabledInputMethodsStr = getEnabledInputMethodsStr(); - if (TextUtils.isEmpty(enabledInputMethodsStr)) { - return imsList; - } - 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 = mSubtypeSplitter.next(); - while (mSubtypeSplitter.hasNext()) { - subtypeHashes.add(mSubtypeSplitter.next()); - } - imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes)); - } - } - return imsList; + return buildInputMethodsAndSubtypeList(getEnabledInputMethodsStr(), + mInputMethodSplitter, + mSubtypeSplitter); } public void appendAndPutEnabledInputMethodLocked(String id, boolean reloadInputMethodStr) { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 30786f0..952b220 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -62,9 +62,10 @@ public class SettingsHelper { */ private static final ArraySet<String> sBroadcastOnRestore; static { - sBroadcastOnRestore = new ArraySet<String>(2); + sBroadcastOnRestore = new ArraySet<String>(3); sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); + sBroadcastOnRestore.add(Settings.Secure.ENABLED_INPUT_METHODS); } private interface SettingsLookup { diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index fd35b5e..4677f65 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -86,7 +86,10 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; +import android.text.TextUtils.SimpleStringSplitter; import android.text.style.SuggestionSpan; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AtomicFile; import android.util.EventLog; import android.util.LruCache; @@ -125,6 +128,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -134,8 +138,12 @@ import java.util.Locale; public class InputMethodManagerService extends IInputMethodManager.Stub implements ServiceConnection, Handler.Callback { static final boolean DEBUG = false; + static final boolean DEBUG_RESTORE = DEBUG || false; static final String TAG = "InputMethodManagerService"; + private static final char INPUT_METHOD_SEPARATOR = ':'; + private static final char INPUT_METHOD_SUBTYPE_SEPARATOR = ';'; + static final int MSG_SHOW_IM_PICKER = 1; static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2; static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 3; @@ -466,12 +474,101 @@ public class InputMethodManagerService extends IInputMethodManager.Stub || Intent.ACTION_USER_REMOVED.equals(action)) { updateCurrentProfileIds(); return; + } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) { + final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME); + if (Settings.Secure.ENABLED_INPUT_METHODS.equals(name)) { + final String prevValue = intent.getStringExtra( + Intent.EXTRA_SETTING_PREVIOUS_VALUE); + final String newValue = intent.getStringExtra( + Intent.EXTRA_SETTING_NEW_VALUE); + restoreEnabledInputMethods(mContext, prevValue, newValue); + } } else { Slog.w(TAG, "Unexpected intent " + intent); } } } + // Apply the results of a restore operation to the set of enabled IMEs. Note that this + // does not attempt to validate on the fly with any installed device policy, so must only + // be run in the context of initial device setup. + // + // TODO: Move this method to InputMethodUtils with adding unit tests. + static void restoreEnabledInputMethods(Context context, String prevValue, String newValue) { + if (DEBUG_RESTORE) { + Slog.i(TAG, "Restoring enabled input methods:"); + Slog.i(TAG, "prev=" + prevValue); + Slog.i(TAG, " new=" + newValue); + } + // 'new' is the just-restored state, 'prev' is what was in settings prior to the restore + ArrayMap<String, ArraySet<String>> prevMap = parseInputMethodsAndSubtypesString(prevValue); + ArrayMap<String, ArraySet<String>> newMap = parseInputMethodsAndSubtypesString(newValue); + + // Merge the restored ime+subtype enabled states into the live state + for (ArrayMap.Entry<String, ArraySet<String>> entry : newMap.entrySet()) { + final String imeId = entry.getKey(); + ArraySet<String> prevSubtypes = prevMap.get(imeId); + if (prevSubtypes == null) { + prevSubtypes = new ArraySet<String>(2); + prevMap.put(imeId, prevSubtypes); + } + prevSubtypes.addAll(entry.getValue()); + } + + final String mergedImesAndSubtypesString = buildInputMethodsAndSubtypesString(prevMap); + if (DEBUG_RESTORE) { + Slog.i(TAG, "Merged IME string:"); + Slog.i(TAG, " " + mergedImesAndSubtypesString); + } + Settings.Secure.putString(context.getContentResolver(), + Settings.Secure.ENABLED_INPUT_METHODS, mergedImesAndSubtypesString); + } + + // TODO: Move this method to InputMethodUtils with adding unit tests. + static String buildInputMethodsAndSubtypesString(ArrayMap<String, ArraySet<String>> map) { + // we want to use the canonical InputMethodSettings implementation, + // so we convert data structures first. + List<Pair<String, ArrayList<String>>> imeMap = + new ArrayList<Pair<String, ArrayList<String>>>(4); + for (ArrayMap.Entry<String, ArraySet<String>> entry : map.entrySet()) { + final String imeName = entry.getKey(); + final ArraySet<String> subtypeSet = entry.getValue(); + final ArrayList<String> subtypes = new ArrayList<String>(2); + if (subtypeSet != null) { + subtypes.addAll(subtypeSet); + } + imeMap.add(new Pair<String, ArrayList<String>>(imeName, subtypes)); + } + return InputMethodSettings.buildInputMethodsSettingString(imeMap); + } + + // TODO: Move this method to InputMethodUtils with adding unit tests. + static ArrayMap<String, ArraySet<String>> parseInputMethodsAndSubtypesString( + final String inputMethodsAndSubtypesString) { + final ArrayMap<String, ArraySet<String>> imeMap = + new ArrayMap<String, ArraySet<String>>(); + if (TextUtils.isEmpty(inputMethodsAndSubtypesString)) { + return imeMap; + } + + final SimpleStringSplitter typeSplitter = + new SimpleStringSplitter(INPUT_METHOD_SEPARATOR); + final SimpleStringSplitter subtypeSplitter = + new SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR); + List<Pair<String, ArrayList<String>>> allImeSettings = + InputMethodSettings.buildInputMethodsAndSubtypeList(inputMethodsAndSubtypesString, + typeSplitter, + subtypeSplitter); + for (Pair<String, ArrayList<String>> ime : allImeSettings) { + ArraySet<String> subtypes = new ArraySet<String>(); + if (ime.second != null) { + subtypes.addAll(ime.second); + } + imeMap.put(ime.first, subtypes); + } + return imeMap; + } + class MyPackageMonitor extends PackageMonitor { private boolean isChangingPackagesOfCurrentUser() { final int userId = getChangingUserId(); @@ -675,6 +772,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); broadcastFilter.addAction(Intent.ACTION_USER_ADDED); broadcastFilter.addAction(Intent.ACTION_USER_REMOVED); + broadcastFilter.addAction(Intent.ACTION_SETTING_RESTORED); mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter); mNotificationShown = false; |
