Merge "Move parseInputMethodsAndSubtypesString to InputMethodUtils."
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index d53efa0..191b5a7 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -29,6 +29,9 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Pair;
 import android.util.Slog;
 import android.view.inputmethod.InputMethodInfo;
@@ -61,6 +64,8 @@
     private static final String TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE =
             "EnabledWhenDefaultIsNotAsciiCapable";
     private static final String TAG_ASCII_CAPABLE = "AsciiCapable";
+    private static final char INPUT_METHOD_SEPARATOR = ':';
+    private static final char INPUT_METHOD_SUBTYPE_SEPARATOR = ';';
     /**
      * Used in {@link #getFallbackLocaleForDefaultIme(ArrayList, Context)} to find the fallback IMEs
      * that are mainly used until the system becomes ready. Note that {@link Locale} in this array
@@ -766,6 +771,40 @@
     }
 
     /**
+     * Parses the setting stored input methods and subtypes string value.
+     *
+     * @param inputMethodsAndSubtypesString The input method subtypes value stored in settings.
+     * @return Map from input method ID to set of input method subtypes IDs.
+     */
+    @VisibleForTesting
+    public static ArrayMap<String, ArraySet<String>> parseInputMethodsAndSubtypesString(
+            @Nullable 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;
+    }
+
+    /**
      * Utility class for putting and getting settings for InputMethod
      * TODO: Move all putters and getters of settings to this class.
      */
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index 67f87a3..e2077a3 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
@@ -23,9 +23,11 @@
 import android.os.Parcel;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+import android.view.inputmethod.InputMethodSubtype;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -947,4 +949,129 @@
             assertEquals(Locale.ENGLISH, locales.get(4));
         }
     }
+
+    public void testParseInputMethodsAndSubtypesString() {
+        // Trivial cases.
+        {
+            assertTrue(InputMethodUtils.parseInputMethodsAndSubtypesString("").isEmpty());
+            assertTrue(InputMethodUtils.parseInputMethodsAndSubtypesString(null).isEmpty());
+        }
+
+        // No subtype cases.
+        {
+            ArrayMap<String, ArraySet<String>> r =
+                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0");
+            assertEquals(1, r.size());
+            assertTrue(r.containsKey("ime0"));
+            assertTrue(r.get("ime0").isEmpty());
+        }
+        {
+            ArrayMap<String, ArraySet<String>> r =
+                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0:ime1");
+            assertEquals(2, r.size());
+            assertTrue(r.containsKey("ime0"));
+            assertTrue(r.get("ime0").isEmpty());
+            assertTrue(r.containsKey("ime1"));
+            assertTrue(r.get("ime1").isEmpty());
+        }
+
+        // Input metho IDs and their subtypes.
+        {
+            ArrayMap<String, ArraySet<String>> r =
+                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0;subtype0");
+            assertEquals(1, r.size());
+            assertTrue(r.containsKey("ime0"));
+            ArraySet<String> subtypes = r.get("ime0");
+            assertEquals(1, subtypes.size());
+            assertTrue(subtypes.contains("subtype0"));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> r =
+                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0;subtype0;subtype0");
+            assertEquals(1, r.size());
+            assertTrue(r.containsKey("ime0"));
+            ArraySet<String> subtypes = r.get("ime0");
+            assertEquals(1, subtypes.size());
+            assertTrue(subtypes.contains("subtype0"));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> r =
+                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0;subtype0;subtype1");
+            assertEquals(1, r.size());
+            assertTrue(r.containsKey("ime0"));
+            ArraySet<String> subtypes = r.get("ime0");
+            assertEquals(2, subtypes.size());
+            assertTrue(subtypes.contains("subtype0"));
+            assertTrue(subtypes.contains("subtype1"));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> r =
+                    InputMethodUtils.parseInputMethodsAndSubtypesString(
+                            "ime0;subtype0:ime1;subtype1");
+            assertEquals(2, r.size());
+            assertTrue(r.containsKey("ime0"));
+            assertTrue(r.containsKey("ime1"));
+            ArraySet<String> subtypes0 = r.get("ime0");
+            assertEquals(1, subtypes0.size());
+            assertTrue(subtypes0.contains("subtype0"));
+
+            ArraySet<String> subtypes1 = r.get("ime1");
+            assertEquals(1, subtypes1.size());
+            assertTrue(subtypes1.contains("subtype1"));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> r =
+                    InputMethodUtils.parseInputMethodsAndSubtypesString(
+                            "ime0;subtype0;subtype1:ime1;subtype2");
+            assertEquals(2, r.size());
+            assertTrue(r.containsKey("ime0"));
+            assertTrue(r.containsKey("ime1"));
+            ArraySet<String> subtypes0 = r.get("ime0");
+            assertEquals(2, subtypes0.size());
+            assertTrue(subtypes0.contains("subtype0"));
+            assertTrue(subtypes0.contains("subtype1"));
+
+            ArraySet<String> subtypes1 = r.get("ime1");
+            assertEquals(1, subtypes1.size());
+            assertTrue(subtypes1.contains("subtype2"));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> r =
+                    InputMethodUtils.parseInputMethodsAndSubtypesString(
+                            "ime0;subtype0;subtype1:ime1;subtype1;subtype2");
+            assertEquals(2, r.size());
+            assertTrue(r.containsKey("ime0"));
+            assertTrue(r.containsKey("ime1"));
+            ArraySet<String> subtypes0 = r.get("ime0");
+            assertEquals(2, subtypes0.size());
+            assertTrue(subtypes0.contains("subtype0"));
+            assertTrue(subtypes0.contains("subtype1"));
+
+            ArraySet<String> subtypes1 = r.get("ime1");
+            assertEquals(2, subtypes1.size());
+            assertTrue(subtypes0.contains("subtype1"));
+            assertTrue(subtypes1.contains("subtype2"));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> r =
+                    InputMethodUtils.parseInputMethodsAndSubtypesString(
+                            "ime0;subtype0;subtype1:ime1;subtype1;subtype2:ime2");
+            assertEquals(3, r.size());
+            assertTrue(r.containsKey("ime0"));
+            assertTrue(r.containsKey("ime1"));
+            assertTrue(r.containsKey("ime2"));
+            ArraySet<String> subtypes0 = r.get("ime0");
+            assertEquals(2, subtypes0.size());
+            assertTrue(subtypes0.contains("subtype0"));
+            assertTrue(subtypes0.contains("subtype1"));
+
+            ArraySet<String> subtypes1 = r.get("ime1");
+            assertEquals(2, subtypes1.size());
+            assertTrue(subtypes0.contains("subtype1"));
+            assertTrue(subtypes1.contains("subtype2"));
+
+            ArraySet<String> subtypes2 = r.get("ime2");
+            assertTrue(subtypes2.isEmpty());
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index f9f617e..66a2308 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -145,9 +145,6 @@
     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_SUBTYPE_PICKER = 1;
     static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 2;
     static final int MSG_SHOW_IM_CONFIG = 3;
@@ -528,8 +525,10 @@
             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);
+        ArrayMap<String, ArraySet<String>> prevMap =
+                InputMethodUtils.parseInputMethodsAndSubtypesString(prevValue);
+        ArrayMap<String, ArraySet<String>> newMap =
+                InputMethodUtils.parseInputMethodsAndSubtypesString(newValue);
 
         // Merge the restored ime+subtype enabled states into the live state
         for (ArrayMap.Entry<String, ArraySet<String>> entry : newMap.entrySet()) {
@@ -568,32 +567,6 @@
         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<>();
-        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<>();
-            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();