OSDN Git Service

Add bluetooth as a supported Slice
authorMatthew Fritze <mfritze@google.com>
Mon, 15 Jan 2018 22:27:30 +0000 (14:27 -0800)
committerMatthew Fritze <mfritze@google.com>
Wed, 24 Jan 2018 00:16:28 +0000 (16:16 -0800)
To add bluetooth as a valid slice requires adding
support for Context-only preference controller
constructors, which was already planned work.

Fixes: 67997327
Test: robotests
Change-Id: I7efd20a05d5796c3327a26b1fc535d5436d1070f

res/xml/connected_devices_advanced.xml
src/com/android/settings/slices/SettingsSliceProvider.java
src/com/android/settings/slices/SliceBuilderUtils.java
tests/robotests/src/com/android/settings/core/XmlControllerAttributeTest.java
tests/robotests/src/com/android/settings/slices/FakeContextOnlyPreferenceController.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java

index dfe23df..7ca890d 100644 (file)
@@ -16,6 +16,7 @@
 
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
     android:key="connected_devices_screen"
     android:title="@string/connected_device_connections_title">
 
@@ -24,6 +25,7 @@
       android:title="@string/bluetooth_settings_title"
       android:icon="@drawable/ic_settings_bluetooth"
       android:summary="@string/bluetooth_pref_summary"
+      settings:controller="com.android.settings.bluetooth.BluetoothSwitchPreferenceController"
       android:order="-7"/>
 
     <SwitchPreference
index e068b2f..433bdf3 100644 (file)
@@ -30,7 +30,6 @@ import android.util.Log;
 import com.android.settings.R;
 import com.android.settingslib.utils.ThreadUtils;
 
-import java.util.HashMap;
 import java.util.Map;
 import java.util.WeakHashMap;
 
index e9152ba..11ff1c1 100644 (file)
@@ -23,6 +23,7 @@ import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
@@ -85,17 +86,46 @@ public class SliceBuilderUtils {
      */
     public static BasePreferenceController getPreferenceController(Context context,
             SliceData sliceData) {
-        // TODO check for context-only controller first.
+        try {
+            return getController(context, sliceData, true /* isContextOnly */);
+        } catch (IllegalStateException e) {
+            // Do nothing
+            Log.d(TAG, "Could not find Context-only controller for preference controller: "
+                    + sliceData.getKey());
+        }
+
+        return getController(context, sliceData, false /* isContextOnly */);
+    }
+
+    /**
+     * Attempts to build a {@link BasePreferenceController} from {@param SliceData}.
+     *
+     * @param sliceData     Backing data for the Slice.
+     * @param contextOnlyCtor {@code true} when the constructor for the
+     *                      {@link BasePreferenceController}
+     *                      only takes a {@link Context}. Else the constructor will be ({@link
+     *                      Context}, {@code String}.
+     */
+    private static BasePreferenceController getController(Context context, SliceData sliceData,
+            boolean contextOnlyCtor) {
         try {
             Class<?> clazz = Class.forName(sliceData.getPreferenceController());
-            Constructor<?> preferenceConstructor = clazz.getConstructor(Context.class,
-                    String.class);
-            return (BasePreferenceController) preferenceConstructor.newInstance(
-                    new Object[]{context, sliceData.getKey()});
+            Constructor<?> preferenceConstructor;
+            Object[] params;
+
+            if (contextOnlyCtor) {
+                preferenceConstructor = clazz.getConstructor(Context.class);
+                params = new Object[]{context};
+            } else {
+                preferenceConstructor = clazz.getConstructor(Context.class, String.class);
+                params = new Object[]{context, sliceData.getKey()};
+            }
+
+            return (BasePreferenceController) preferenceConstructor.newInstance(params);
         } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException |
                 IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
             throw new IllegalStateException(
-                    "Invalid preference controller: " + sliceData.getPreferenceController());
+                    "Invalid preference controller: " + sliceData.getPreferenceController(), e);
         }
     }
 
index 8ded9d6..e6ca59b 100644 (file)
@@ -80,11 +80,6 @@ public class XmlControllerAttributeTest {
     private static final String BAD_CLASSNAME_ERROR =
             "The following controllers set in the XML did not have valid class names:\n";
 
-    private static final String BAD_CONSTRUCTOR_ERROR =
-            "The constructor provided by the following classes were insufficient to instantiate "
-                    + "the object. It could be due to being an interface, abstract, or an "
-                    + "IllegalAccessException. Please fix the following classes:\n";
-
     Context mContext;
     SearchFeatureProvider mSearchProvider;
     private FakeFeatureFactory mFakeFeatureFactory;
@@ -112,7 +107,6 @@ public class XmlControllerAttributeTest {
         Set<String> invalidConstructors = new HashSet<>();
         Set<String> invalidClassHierarchy = new HashSet<>();
         Set<String> badClassNameControllers = new HashSet<>();
-        Set<String> badConstructorControllers = new HashSet<>();
 
         for (int resId : xmlSet) {
             xmlControllers.addAll(getXmlControllers(resId));
@@ -133,13 +127,7 @@ public class XmlControllerAttributeTest {
                 continue;
             }
 
-            Object controller = getObjectFromConstructor(constructor);
-            if (controller == null) {
-                badConstructorControllers.add(controllerClassName);
-                continue;
-            }
-
-            if (!(controller instanceof BasePreferenceController)) {
+            if (!isBasePreferenceController(clazz)) {
                 invalidClassHierarchy.add(controllerClassName);
             }
         }
@@ -150,13 +138,10 @@ public class XmlControllerAttributeTest {
                 invalidClassHierarchy);
         final String badClassNameError = buildErrorMessage(BAD_CLASSNAME_ERROR,
                 badClassNameControllers);
-        final String badConstructorError = buildErrorMessage(BAD_CONSTRUCTOR_ERROR,
-                badConstructorControllers);
 
         assertWithMessage(invalidConstructorError).that(invalidConstructors).isEmpty();
         assertWithMessage(invalidClassHierarchyError).that(invalidClassHierarchy).isEmpty();
         assertWithMessage(badClassNameError).that(badClassNameControllers).isEmpty();
-        assertWithMessage(badConstructorError).that(badConstructorControllers).isEmpty();
     }
 
     private Set<Integer> getIndexableXml() {
@@ -260,25 +245,16 @@ public class XmlControllerAttributeTest {
         return constructor;
     }
 
-    private Object getObjectFromConstructor(Constructor<?> constructor) {
-        Object controller = null;
-
-        try {
-            controller = constructor.newInstance(mContext);
-        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
-                IllegalArgumentException e) {
-        }
-
-        if (controller != null) {
-            return controller;
-        }
-
-        try {
-            controller = constructor.newInstance(mContext, "key");
-        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
-                IllegalArgumentException e) {
+    /**
+     * Make sure that {@link BasePreferenceController} is in the class hierarchy.
+     */
+    private boolean isBasePreferenceController(Class<?> clazz) {
+        while (clazz != null) {
+            clazz = clazz.getSuperclass();
+            if (BasePreferenceController.class.equals(clazz)) {
+                return true;
+            }
         }
-
-        return controller;
+        return false;
     }
-}
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/slices/FakeContextOnlyPreferenceController.java b/tests/robotests/src/com/android/settings/slices/FakeContextOnlyPreferenceController.java
new file mode 100644 (file)
index 0000000..214607b
--- /dev/null
@@ -0,0 +1,19 @@
+package com.android.settings.slices;
+
+import android.content.Context;
+
+import com.android.settings.core.BasePreferenceController;
+
+public class FakeContextOnlyPreferenceController extends BasePreferenceController {
+
+    public static final String KEY = "fakeController2";
+
+    public FakeContextOnlyPreferenceController(Context context) {
+        super(context, KEY);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+}
\ No newline at end of file
index 10e4b76..0923571 100644 (file)
@@ -50,8 +50,8 @@ public class SliceBuilderUtilsTest {
     private final String FRAGMENT_NAME = "fragment name";
     private final int ICON = 1234; // I declare a thumb war
     private final Uri URI = Uri.parse("content://com.android.settings.slices/test");
-    private final String PREF_CONTROLLER = FakeToggleController.class.getName();
-    ;
+    private final Class PREF_CONTROLLER = FakeToggleController.class;
+    private final Class PREF_CONTROLLER2 = FakeContextOnlyPreferenceController.class;
 
     private Context mContext;
 
@@ -76,6 +76,14 @@ public class SliceBuilderUtilsTest {
     }
 
     @Test
+    public void testGetPreferenceController_contextOnly_buildsMatchingController() {
+        BasePreferenceController controller = SliceBuilderUtils.getPreferenceController(mContext,
+                getDummyData(PREF_CONTROLLER2));
+
+        assertThat(controller).isInstanceOf(FakeContextOnlyPreferenceController.class);
+    }
+
+    @Test
     public void testDynamicSummary_returnsSliceSummary() {
         SliceData data = getDummyData();
         FakePreferenceController controller = new FakePreferenceController(mContext, KEY);
@@ -87,7 +95,7 @@ public class SliceBuilderUtilsTest {
 
     @Test
     public void testDynamicSummary_returnsFragmentSummary() {
-        SliceData data = getDummyData(null);
+        SliceData data = getDummyData((String) null);
         FakePreferenceController controller = spy(new FakePreferenceController(mContext, KEY));
         String controllerSummary = "new_Summary";
         doReturn(controllerSummary).when(controller).getSummary();
@@ -99,7 +107,7 @@ public class SliceBuilderUtilsTest {
 
     @Test
     public void testDynamicSummary_returnsSliceScreenTitle() {
-        SliceData data = getDummyData(null);
+        SliceData data = getDummyData((String) null);
         FakePreferenceController controller = new FakePreferenceController(mContext, KEY);
 
         String summary = SliceBuilderUtils.getSubtitleText(mContext, controller, data);
@@ -129,10 +137,18 @@ public class SliceBuilderUtilsTest {
     }
 
     private SliceData getDummyData() {
-        return getDummyData(SUMMARY);
+        return getDummyData(PREF_CONTROLLER, SUMMARY);
     }
 
     private SliceData getDummyData(String summary) {
+        return getDummyData(PREF_CONTROLLER, summary);
+    }
+
+    private SliceData getDummyData(Class prefController) {
+        return getDummyData(prefController, SUMMARY);
+    }
+
+    private SliceData getDummyData(Class prefController, String summary) {
         return new SliceData.Builder()
                 .setKey(KEY)
                 .setTitle(TITLE)
@@ -141,7 +157,7 @@ public class SliceBuilderUtilsTest {
                 .setIcon(ICON)
                 .setFragmentName(FRAGMENT_NAME)
                 .setUri(URI)
-                .setPreferenceControllerClassName(PREF_CONTROLLER)
+                .setPreferenceControllerClassName(prefController.getName())
                 .build();
     }
 }