<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">
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
import com.android.settings.R;
import com.android.settingslib.utils.ThreadUtils;
-import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
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;
*/
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);
}
}
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;
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));
continue;
}
- Object controller = getObjectFromConstructor(constructor);
- if (controller == null) {
- badConstructorControllers.add(controllerClassName);
- continue;
- }
-
- if (!(controller instanceof BasePreferenceController)) {
+ if (!isBasePreferenceController(clazz)) {
invalidClassHierarchy.add(controllerClassName);
}
}
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() {
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
--- /dev/null
+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
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;
}
@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);
@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();
@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);
}
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)
.setIcon(ICON)
.setFragmentName(FRAGMENT_NAME)
.setUri(URI)
- .setPreferenceControllerClassName(PREF_CONTROLLER)
+ .setPreferenceControllerClassName(prefController.getName())
.build();
}
}