Change underlying preferenceControllers to an ArrayMap of Lists.

Previously, DashboardFragment stores preference controllers in an
ArrayMap of <Class, AbstractPreferenceController>. Thus when there are
multiple controllers of same class (ie. multiple
PreferenceCategoryControllers), they cannot be stored simultaneously.
This changes the value to a List so we can store multiple controllers of
the same Class without extraneous sub-classing.

Bug: 70949662
Test: All existing tests still pass.
Added
DashboardFragmentTest#testPreferenceControllerSetter_shouldAddAndNotReplace
and DashboardFragmentTest#updateState_doesNotSkipControllersOfSameClass.
Change-Id: I397e8c91977ea1180d48a3af75dd4058bf1175c0
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index 3e1e881..6a88a38 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -54,7 +54,7 @@
         SummaryLoader.SummaryConsumer {
     private static final String TAG = "DashboardFragment";
 
-    private final Map<Class, AbstractPreferenceController> mPreferenceControllers =
+    private final Map<Class, List<AbstractPreferenceController>> mPreferenceControllers =
             new ArrayMap<>();
     private final Set<String> mDashboardTilePrefKeys = new ArraySet<>();
 
@@ -156,14 +156,17 @@
 
     @Override
     public boolean onPreferenceTreeClick(Preference preference) {
-        Collection<AbstractPreferenceController> controllers = mPreferenceControllers.values();
+        Collection<List<AbstractPreferenceController>> controllers =
+                mPreferenceControllers.values();
         // If preference contains intent, log it before handling.
         mMetricsFeatureProvider.logDashboardStartIntent(
                 getContext(), preference.getIntent(), getMetricsCategory());
         // Give all controllers a chance to handle click.
-        for (AbstractPreferenceController controller : controllers) {
-            if (controller.handlePreferenceTreeClick(preference)) {
-                return true;
+        for (List<AbstractPreferenceController> controllerList : controllers) {
+            for (AbstractPreferenceController controller : controllerList) {
+                if (controller.handlePreferenceTreeClick(preference)) {
+                    return true;
+                }
             }
         }
         return super.onPreferenceTreeClick(preference);
@@ -189,12 +192,23 @@
     protected abstract int getPreferenceScreenResId();
 
     protected <T extends AbstractPreferenceController> T getPreferenceController(Class<T> clazz) {
-        AbstractPreferenceController controller = mPreferenceControllers.get(clazz);
-        return (T) controller;
+        List<AbstractPreferenceController> controllerList = mPreferenceControllers.get(clazz);
+        if (controllerList != null) {
+            if (controllerList.size() > 1) {
+                Log.w(TAG, "Multiple controllers of Class " + clazz.getSimpleName()
+                        + " found, returning first one.");
+            }
+            return (T) controllerList.get(0);
+        }
+
+        return null;
     }
 
     protected void addPreferenceController(AbstractPreferenceController controller) {
-        mPreferenceControllers.put(controller.getClass(), controller);
+        if (mPreferenceControllers.get(controller.getClass()) == null) {
+            mPreferenceControllers.put(controller.getClass(), new ArrayList<>());
+        }
+        mPreferenceControllers.get(controller.getClass()).add(controller);
     }
 
     /**
@@ -249,31 +263,32 @@
         }
         addPreferencesFromResource(resId);
         final PreferenceScreen screen = getPreferenceScreen();
-        Collection<AbstractPreferenceController> controllers = mPreferenceControllers.values();
-        for (AbstractPreferenceController controller : controllers) {
-            controller.displayPreference(screen);
-        }
+        mPreferenceControllers.values().stream().flatMap(Collection::stream).forEach(
+                controller -> controller.displayPreference(screen));
     }
 
     /**
      * Update state of each preference managed by PreferenceController.
      */
     protected void updatePreferenceStates() {
-        Collection<AbstractPreferenceController> controllers = mPreferenceControllers.values();
         final PreferenceScreen screen = getPreferenceScreen();
-        for (AbstractPreferenceController controller : controllers) {
-            if (!controller.isAvailable()) {
-                continue;
-            }
-            final String key = controller.getPreferenceKey();
+        Collection<List<AbstractPreferenceController>> controllerLists =
+                mPreferenceControllers.values();
+        for (List<AbstractPreferenceController> controllerList : controllerLists) {
+            for (AbstractPreferenceController controller : controllerList) {
+                if (!controller.isAvailable()) {
+                    continue;
+                }
+                final String key = controller.getPreferenceKey();
 
-            final Preference preference = screen.findPreference(key);
-            if (preference == null) {
-                Log.d(TAG, String.format("Cannot find preference with key %s in Controller %s",
-                        key, controller.getClass().getSimpleName()));
-                continue;
+                final Preference preference = screen.findPreference(key);
+                if (preference == null) {
+                    Log.d(TAG, String.format("Cannot find preference with key %s in Controller %s",
+                            key, controller.getClass().getSimpleName()));
+                    continue;
+                }
+                controller.updateState(preference);
             }
-            controller.updateState(preference);
         }
     }
 
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
index c330340..6c663ab 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
@@ -91,6 +91,19 @@
     }
 
     @Test
+    public void testPreferenceControllerSetter_shouldAddAndNotReplace() {
+        final TestPreferenceController controller1 = new TestPreferenceController(mContext);
+        mTestFragment.addPreferenceController(controller1);
+        final TestPreferenceController controller2 = new TestPreferenceController(mContext);
+        mTestFragment.addPreferenceController(controller2);
+
+        final TestPreferenceController retrievedController = mTestFragment.getPreferenceController
+                (TestPreferenceController.class);
+
+        assertThat(controller1).isSameAs(retrievedController);
+    }
+
+    @Test
     public void displayTilesAsPreference_shouldAddTilesWithIntent() {
         when(mFakeFeatureFactory.dashboardFeatureProvider
                 .getTilesForCategory(nullable(String.class)))
@@ -146,6 +159,23 @@
     }
 
     @Test
+    public void updateState_doesNotSkipControllersOfSameClass() {
+        final AbstractPreferenceController mockController1 =
+                mock(AbstractPreferenceController.class);
+        final AbstractPreferenceController mockController2 =
+                mock(AbstractPreferenceController.class);
+        mTestFragment.addPreferenceController(mockController1);
+        mTestFragment.addPreferenceController(mockController2);
+        when(mockController1.isAvailable()).thenReturn(true);
+        when(mockController2.isAvailable()).thenReturn(true);
+
+        mTestFragment.updatePreferenceStates();
+
+        verify(mockController1).getPreferenceKey();
+        verify(mockController2).getPreferenceKey();
+    }
+
+    @Test
     public void tintTileIcon_hasMetadata_shouldReturnIconTintableMetadata() {
         final Tile tile = new Tile();
         tile.icon = mock(Icon.class);
diff --git a/tests/robotests/src/com/android/settings/security/screenlock/ScreenLockSettingsTest.java b/tests/robotests/src/com/android/settings/security/screenlock/ScreenLockSettingsTest.java
index 4213fc5..374b7ec 100644
--- a/tests/robotests/src/com/android/settings/security/screenlock/ScreenLockSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/security/screenlock/ScreenLockSettingsTest.java
@@ -33,6 +33,8 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 @RunWith(SettingsRobolectricTestRunner.class)
@@ -55,10 +57,12 @@
 
     @Test
     public void onOwnerInfoUpdated_shouldUpdateOwnerInfoController() {
-        final Map<Class, AbstractPreferenceController> preferenceControllers =
+        final Map<Class, List<AbstractPreferenceController>> preferenceControllers =
                 ReflectionHelpers.getField(mSettings, "mPreferenceControllers");
         final OwnerInfoPreferenceController controller = mock(OwnerInfoPreferenceController.class);
-        preferenceControllers.put(OwnerInfoPreferenceController.class, controller);
+        List<AbstractPreferenceController> controllerList = new ArrayList<>();
+        controllerList.add(controller);
+        preferenceControllers.put(OwnerInfoPreferenceController.class, controllerList);
 
         mSettings.onOwnerInfoUpdated();