Merge "Support Slice type in SliceData"

This commit is contained in:
TreeHugger Robot
2018-03-02 00:48:08 +00:00
committed by Android (Google) Code Review
11 changed files with 180 additions and 66 deletions

View File

@@ -20,6 +20,7 @@ import android.util.Log;
import com.android.settings.search.ResultPayload; import com.android.settings.search.ResultPayload;
import com.android.settings.search.SearchIndexableRaw; import com.android.settings.search.SearchIndexableRaw;
import com.android.settings.slices.SliceData;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -153,6 +154,14 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl
return getAvailabilityStatus() != DISABLED_UNSUPPORTED; return getAvailabilityStatus() != DISABLED_UNSUPPORTED;
} }
/**
* @return the UI type supported by the controller.
*/
@SliceData.SliceType
public int getSliceType() {
return SliceData.SliceType.INTENT;
}
/** /**
* Updates non-indexable keys for search provider. * Updates non-indexable keys for search provider.
* *

View File

@@ -17,6 +17,7 @@ import android.content.Context;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.support.v7.preference.TwoStatePreference; import android.support.v7.preference.TwoStatePreference;
import com.android.settings.slices.SliceData;
import com.android.settings.widget.MasterSwitchPreference; import com.android.settings.widget.MasterSwitchPreference;
/** /**
@@ -59,4 +60,11 @@ public abstract class TogglePreferenceController extends BasePreferenceControlle
public final boolean onPreferenceChange(Preference preference, Object newValue) { public final boolean onPreferenceChange(Preference preference, Object newValue) {
return setChecked((Boolean) newValue); return setChecked((Boolean) newValue);
} }
@Override
@SliceData.SliceType
public int getSliceType() {
return SliceData.SliceType.SWITCH;
}
} }

View File

@@ -23,7 +23,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R; import com.android.settings.R;
@@ -67,7 +66,7 @@ public class SliceBuilderUtils {
// TODO (b/71640747) Respect setting availability. // TODO (b/71640747) Respect setting availability.
if (controller instanceof TogglePreferenceController) { if (sliceData.getSliceType() == SliceData.SliceType.SWITCH) {
addToggleAction(context, builder, ((TogglePreferenceController) controller).isChecked(), addToggleAction(context, builder, ((TogglePreferenceController) controller).isChecked(),
sliceData.getKey()); sliceData.getKey());
} }
@@ -77,23 +76,36 @@ public class SliceBuilderUtils {
.build(); .build();
} }
/**
* @return the {@link SliceData.SliceType} for the {@param controllerClassName} and key.
*/
@SliceData.SliceType
public static int getSliceType(Context context, String controllerClassName,
String controllerKey) {
BasePreferenceController controller = getPreferenceController(context, controllerClassName,
controllerKey);
return controller.getSliceType();
}
/** /**
* Looks at the {@link SliceData#preferenceController} from {@param sliceData} and attempts to * Looks at the {@link SliceData#preferenceController} from {@param sliceData} and attempts to
* build an {@link AbstractPreferenceController}. * build an {@link AbstractPreferenceController}.
*/ */
public static BasePreferenceController getPreferenceController(Context context, public static BasePreferenceController getPreferenceController(Context context,
SliceData sliceData) { SliceData sliceData) {
return getPreferenceController(context, sliceData.getPreferenceController(),
sliceData.getKey());
}
private static BasePreferenceController getPreferenceController(Context context,
String controllerClassName, String controllerKey) {
try { try {
return BasePreferenceController.createInstance(context, return BasePreferenceController.createInstance(context, controllerClassName);
sliceData.getPreferenceController());
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
// Do nothing // Do nothing
Log.d(TAG, "Could not find Context-only controller for preference controller: "
+ sliceData.getKey());
} }
return BasePreferenceController.createInstance(context, sliceData.getPreferenceController(), return BasePreferenceController.createInstance(context, controllerClassName, controllerKey);
sliceData.getKey());
} }
private static void addToggleAction(Context context, RowBuilder builder, boolean isChecked, private static void addToggleAction(Context context, RowBuilder builder, boolean isChecked,

View File

@@ -16,76 +16,111 @@
package com.android.settings.slices; package com.android.settings.slices;
import android.annotation.IntDef;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** /**
* Data class representing a slice stored by {@link SlicesIndexer}. * Data class representing a slice stored by {@link SlicesIndexer}.
* Note that {@link #key} is treated as a primary key for this class and determines equality. * Note that {@link #mKey} is treated as a primary key for this class and determines equality.
*/ */
public class SliceData { public class SliceData {
private final String key; /**
* Flags indicating the UI type of the Slice.
*/
@IntDef({SliceType.INTENT, SliceType.SWITCH, SliceType.SLIDER})
@Retention(RetentionPolicy.SOURCE)
public @interface SliceType {
/**
* Only supports content intent.
*/
int INTENT = 0;
private final String title; /**
* Supports toggle action.
*/
int SWITCH = 1;
private final String summary; /**
* Supports progress bar.
*/
int SLIDER = 2;
}
private final String screenTitle; private final String mKey;
private final int iconResource; private final String mTitle;
private final String fragmentClassName; private final String mSummary;
private final Uri uri; private final String mScreenTitle;
private final String preferenceController; private final int mIconResource;
private final String mFragmentClassName;
private final Uri mUri;
private final String mPreferenceController;
@SliceType
private final int mSliceType;
public String getKey() { public String getKey() {
return key; return mKey;
} }
public String getTitle() { public String getTitle() {
return title; return mTitle;
} }
public String getSummary() { public String getSummary() {
return summary; return mSummary;
} }
public String getScreenTitle() { public String getScreenTitle() {
return screenTitle; return mScreenTitle;
} }
public int getIconResource() { public int getIconResource() {
return iconResource; return mIconResource;
} }
public String getFragmentClassName() { public String getFragmentClassName() {
return fragmentClassName; return mFragmentClassName;
} }
public Uri getUri() { public Uri getUri() {
return uri; return mUri;
} }
public String getPreferenceController() { public String getPreferenceController() {
return preferenceController; return mPreferenceController;
}
public int getSliceType() {
return mSliceType;
} }
private SliceData(Builder builder) { private SliceData(Builder builder) {
key = builder.mKey; mKey = builder.mKey;
title = builder.mTitle; mTitle = builder.mTitle;
summary = builder.mSummary; mSummary = builder.mSummary;
screenTitle = builder.mScreenTitle; mScreenTitle = builder.mScreenTitle;
iconResource = builder.mIconResource; mIconResource = builder.mIconResource;
fragmentClassName = builder.mFragmentClassName; mFragmentClassName = builder.mFragmentClassName;
uri = builder.mUri; mUri = builder.mUri;
preferenceController = builder.mPrefControllerClassName; mPreferenceController = builder.mPrefControllerClassName;
mSliceType = builder.mSliceType;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return key.hashCode(); return mKey.hashCode();
} }
@Override @Override
@@ -94,7 +129,7 @@ public class SliceData {
return false; return false;
} }
SliceData newObject = (SliceData) obj; SliceData newObject = (SliceData) obj;
return TextUtils.equals(key, newObject.key); return TextUtils.equals(mKey, newObject.mKey);
} }
static class Builder { static class Builder {
@@ -114,6 +149,8 @@ public class SliceData {
private String mPrefControllerClassName; private String mPrefControllerClassName;
private int mSliceType;
public Builder setKey(String key) { public Builder setKey(String key) {
mKey = key; mKey = key;
return this; return this;
@@ -154,6 +191,11 @@ public class SliceData {
return this; return this;
} }
public Builder setSliceType(@SliceType int sliceType) {
mSliceType = sliceType;
return this;
}
public SliceData build() { public SliceData build() {
if (TextUtils.isEmpty(mKey)) { if (TextUtils.isEmpty(mKey)) {
throw new IllegalStateException("Key cannot be empty"); throw new IllegalStateException("Key cannot be empty");

View File

@@ -127,11 +127,6 @@ class SliceDataConverter {
XmlResourceParser parser = null; XmlResourceParser parser = null;
final List<SliceData> xmlSliceData = new ArrayList<>(); final List<SliceData> xmlSliceData = new ArrayList<>();
String key;
String title;
String summary;
@DrawableRes int iconResId;
String controllerClassName;
try { try {
parser = mContext.getResources().getXml(xmlResId); parser = mContext.getResources().getXml(xmlResId);
@@ -155,30 +150,33 @@ class SliceDataConverter {
// TODO (b/67996923) Investigate if we need headers for Slices, since they never // TODO (b/67996923) Investigate if we need headers for Slices, since they never
// correspond to an actual setting. // correspond to an actual setting.
SliceData xmlSlice;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue; continue;
} }
// TODO (b/67996923) Non-controller Slices should become intent-only slices. // TODO (b/67996923) Non-controller Slices should become intent-only slices.
// Note that without a controller, dynamic summaries are impossible. // Note that without a controller, dynamic summaries are impossible.
// TODO (b/67996923) This will not work if preferences have nested intents: // TODO (b/67996923) This will not work if preferences have nested intents:
// <pref ....> // <pref ....>
// <intent action="blab"/> </pref> // <intent action="blab"/> </pref>
controllerClassName = PreferenceXmlParserUtils.getController(mContext, attrs); final String controllerClassName = PreferenceXmlParserUtils.getController(mContext,
attrs);
if (TextUtils.isEmpty(controllerClassName)) { if (TextUtils.isEmpty(controllerClassName)) {
continue; continue;
} }
title = PreferenceXmlParserUtils.getDataTitle(mContext, attrs); final String title = PreferenceXmlParserUtils.getDataTitle(mContext, attrs);
key = PreferenceXmlParserUtils.getDataKey(mContext, attrs); final String key = PreferenceXmlParserUtils.getDataKey(mContext, attrs);
iconResId = PreferenceXmlParserUtils.getDataIcon(mContext, attrs); @DrawableRes final int iconResId = PreferenceXmlParserUtils.getDataIcon(mContext,
summary = PreferenceXmlParserUtils.getDataSummary(mContext, attrs); attrs);
final String summary = PreferenceXmlParserUtils.getDataSummary(mContext, attrs);
final int sliceType = SliceBuilderUtils.getSliceType(mContext, controllerClassName,
key);
xmlSlice = new SliceData.Builder() final SliceData xmlSlice = new SliceData.Builder()
.setKey(key) .setKey(key)
.setTitle(title) .setTitle(title)
.setSummary(summary) .setSummary(summary)
@@ -186,6 +184,7 @@ class SliceDataConverter {
.setScreenTitle(screenTitle) .setScreenTitle(screenTitle)
.setPreferenceControllerClassName(controllerClassName) .setPreferenceControllerClassName(controllerClassName)
.setFragmentName(fragmentName) .setFragmentName(fragmentName)
.setSliceType(sliceType)
.build(); .build();
xmlSliceData.add(xmlSlice); xmlSliceData.add(xmlSlice);

View File

@@ -1 +1,2 @@
com.android.settings.testutils.FakeToggleController com.android.settings.testutils.FakeToggleController
com.android.settings.core.TogglePreferenceControllerTest$FakeToggle

View File

@@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.slices.SliceData;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before; import org.junit.Before;
@@ -126,4 +127,9 @@ public class BasePreferenceControllerTest {
assertThat(mPreferenceController.isSupported()).isTrue(); assertThat(mPreferenceController.isSupported()).isTrue();
} }
@Test
public void getSliceType_shouldReturnIntent() {
assertThat(mPreferenceController.getSliceType()).isEqualTo(SliceData.SliceType.INTENT);
}
} }

View File

@@ -17,20 +17,16 @@ package com.android.settings.core;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.support.v14.preference.SwitchPreference; import android.support.v14.preference.SwitchPreference;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.slices.SliceData;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
@@ -38,35 +34,34 @@ import org.robolectric.annotation.Config;
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class TogglePreferenceControllerTest { public class TogglePreferenceControllerTest {
@Mock FakeToggle mToggleController;
TogglePreferenceController mTogglePreferenceController;
Context mContext; Context mContext;
SwitchPreference mPreference; SwitchPreference mPreference;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application; mContext = RuntimeEnvironment.application;
mPreference = new SwitchPreference(mContext); mPreference = new SwitchPreference(mContext);
mToggleController = new FakeToggle(mContext, "key");
} }
@Test @Test
public void testSetsPreferenceValue_setsChecked() { public void testSetsPreferenceValue_setsChecked() {
when(mTogglePreferenceController.isChecked()).thenReturn(true); mToggleController.setChecked(true);
mPreference.setChecked(false); mPreference.setChecked(false);
mTogglePreferenceController.updateState(mPreference); mToggleController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue(); assertThat(mPreference.isChecked()).isTrue();
} }
@Test @Test
public void testSetsPreferenceValue_setsNotChecked() { public void testSetsPreferenceValue_setsNotChecked() {
when(mTogglePreferenceController.isChecked()).thenReturn(false); mToggleController.setChecked(false);
mPreference.setChecked(true); mPreference.setChecked(true);
mTogglePreferenceController.updateState(mPreference); mToggleController.updateState(mPreference);
assertThat(mPreference.isChecked()).isFalse(); assertThat(mPreference.isChecked()).isFalse();
} }
@@ -74,18 +69,51 @@ public class TogglePreferenceControllerTest {
@Test @Test
public void testUpdatesPreferenceOnChange_turnsOn() { public void testUpdatesPreferenceOnChange_turnsOn() {
boolean newValue = true; boolean newValue = true;
mToggleController.setChecked(!newValue);
mTogglePreferenceController.onPreferenceChange(mPreference, newValue); mToggleController.onPreferenceChange(mPreference, newValue);
verify(mTogglePreferenceController).setChecked(newValue); assertThat(mToggleController.isChecked()).isEqualTo(newValue);
} }
@Test @Test
public void testUpdatesPreferenceOnChange_turnsOff() { public void testUpdatesPreferenceOnChange_turnsOff() {
boolean newValue = false; boolean newValue = false;
mToggleController.setChecked(!newValue);
mTogglePreferenceController.onPreferenceChange(mPreference, newValue); mToggleController.onPreferenceChange(mPreference, newValue);
verify(mTogglePreferenceController).setChecked(newValue); assertThat(mToggleController.isChecked()).isEqualTo(newValue);
}
@Test
public void testSliceType_returnsSliceType() {
assertThat(mToggleController.getSliceType()).isEqualTo(
SliceData.SliceType.SWITCH);
}
private static class FakeToggle extends TogglePreferenceController {
private boolean checkedFlag;
public FakeToggle(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public boolean isChecked() {
return checkedFlag;
}
@Override
public boolean setChecked(boolean isChecked) {
checkedFlag = isChecked;
return true;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
} }
} }

View File

@@ -30,4 +30,9 @@ public class FakePreferenceController extends BasePreferenceController {
public int getAvailabilityStatus() { public int getAvailabilityStatus() {
return AVAILABLE; return AVAILABLE;
} }
@Override
public int getSliceType() {
return SliceData.SliceType.SLIDER;
}
} }

View File

@@ -90,5 +90,6 @@ public class SliceDataConverterTest {
assertThat(fakeSlice.getUri()).isNull(); assertThat(fakeSlice.getUri()).isNull();
assertThat(fakeSlice.getFragmentClassName()).isEqualTo(fakeFragmentClassName); assertThat(fakeSlice.getFragmentClassName()).isEqualTo(fakeFragmentClassName);
assertThat(fakeSlice.getPreferenceController()).isEqualTo(fakeControllerName); assertThat(fakeSlice.getPreferenceController()).isEqualTo(fakeControllerName);
assertThat(fakeSlice.getSliceType()).isEqualTo(SliceData.SliceType.SLIDER);
} }
} }

View File

@@ -40,6 +40,7 @@ public class SliceDataTest {
private final int ICON = 1234; // I declare a thumb war private final int ICON = 1234; // I declare a thumb war
private final Uri URI = Uri.parse("content://com.android.settings.slices/test"); private final Uri URI = Uri.parse("content://com.android.settings.slices/test");
private final String PREF_CONTROLLER = "com.android.settings.slices.tester"; private final String PREF_CONTROLLER = "com.android.settings.slices.tester";
private final int SLICE_TYPE = SliceData.SliceType.SWITCH;
@Test @Test
public void testBuilder_buildsMatchingObject() { public void testBuilder_buildsMatchingObject() {
@@ -51,7 +52,8 @@ public class SliceDataTest {
.setIcon(ICON) .setIcon(ICON)
.setFragmentName(FRAGMENT_NAME) .setFragmentName(FRAGMENT_NAME)
.setUri(URI) .setUri(URI)
.setPreferenceControllerClassName(PREF_CONTROLLER); .setPreferenceControllerClassName(PREF_CONTROLLER)
.setSliceType(SLICE_TYPE);
SliceData data = builder.build(); SliceData data = builder.build();
@@ -63,6 +65,7 @@ public class SliceDataTest {
assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME); assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME);
assertThat(data.getUri()).isEqualTo(URI); assertThat(data.getUri()).isEqualTo(URI);
assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER); assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER);
assertThat(data.getSliceType()).isEqualTo(SLICE_TYPE);
} }
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)