Move parseInputMethodsAndSubtypesString to InputMethodUtils.

This CL is mechanical code moving and does not change any existing
semantics.

parseInputMethodsAndSubtypesString is introduced by
If0104151b3526da6ecc669adde3119a239ecafeb for addressing Bug 19822542.
This code moving is one of the TODOs in above change.

Bug: 22285167
Change-Id: I01f5fafbbcfe3e3f5313829162ec011eaf2ad991
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();