Restrict toggle/slider slice when the preference restricted
Bug: 289980550
Test: robotest & manual
Change-Id: Id87fbf12a2722344dd07886e810e7c61a9f401aa
diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml
index c3e0eef..57e3249 100644
--- a/res/xml/sound_settings.xml
+++ b/res/xml/sound_settings.xml
@@ -36,6 +36,7 @@
android:icon="@drawable/ic_media_stream"
android:title="@string/media_volume_option_title"
android:order="-180"
+ settings:userRestriction="no_adjust_volume"
settings:controller="com.android.settings.notification.MediaVolumePreferenceController"/>
<!-- Media output switcher -->
@@ -53,6 +54,7 @@
android:icon="@drawable/ic_local_phone_24_lib"
android:title="@string/call_volume_option_title"
android:order="-170"
+ settings:userRestriction="no_adjust_volume"
settings:controller="com.android.settings.notification.CallVolumePreferenceController"/>
<!-- Hands free profile output switcher -->
@@ -70,6 +72,7 @@
android:icon="@drawable/ic_ring_volume"
android:title="@string/separate_ring_volume_option_title"
android:order="-155"
+ settings:userRestriction="no_adjust_volume"
settings:controller="com.android.settings.notification.SeparateRingVolumePreferenceController"/>
<!-- Notification volume -->
@@ -78,6 +81,7 @@
android:icon="@drawable/ic_notifications"
android:title="@string/notification_volume_option_title"
android:order="-150"
+ settings:userRestriction="no_adjust_volume"
settings:controller="com.android.settings.notification.NotificationVolumePreferenceController"
settings:unavailableSliceSubtitle="@string/notification_volume_disabled_summary"/>
@@ -87,6 +91,7 @@
android:icon="@*android:drawable/ic_audio_alarm"
android:title="@string/alarm_volume_option_title"
android:order="-140"
+ settings:userRestriction="no_adjust_volume"
settings:controller="com.android.settings.notification.AlarmVolumePreferenceController"/>
<!-- TODO(b/174964721): make this a PrimarySwitchPreference -->
diff --git a/src/com/android/settings/core/PreferenceXmlParserUtils.java b/src/com/android/settings/core/PreferenceXmlParserUtils.java
index a1a8d67..37d9144 100644
--- a/src/com/android/settings/core/PreferenceXmlParserUtils.java
+++ b/src/com/android/settings/core/PreferenceXmlParserUtils.java
@@ -74,7 +74,8 @@
MetadataFlag.FLAG_NEED_SEARCHABLE,
MetadataFlag.FLAG_UNAVAILABLE_SLICE_SUBTITLE,
MetadataFlag.FLAG_FOR_WORK,
- MetadataFlag.FLAG_NEED_HIGHLIGHTABLE_MENU_KEY})
+ MetadataFlag.FLAG_NEED_HIGHLIGHTABLE_MENU_KEY,
+ MetadataFlag.FLAG_NEED_USER_RESTRICTION})
@Retention(RetentionPolicy.SOURCE)
public @interface MetadataFlag {
@@ -91,6 +92,7 @@
int FLAG_UNAVAILABLE_SLICE_SUBTITLE = 1 << 11;
int FLAG_FOR_WORK = 1 << 12;
int FLAG_NEED_HIGHLIGHTABLE_MENU_KEY = 1 << 13;
+ int FLAG_NEED_USER_RESTRICTION = 1 << 14;
}
public static final String METADATA_PREF_TYPE = "type";
@@ -105,6 +107,7 @@
public static final String METADATA_UNAVAILABLE_SLICE_SUBTITLE = "unavailable_slice_subtitle";
public static final String METADATA_FOR_WORK = "for_work";
public static final String METADATA_HIGHLIGHTABLE_MENU_KEY = "highlightable_menu_key";
+ public static final String METADATA_USER_RESTRICTION = "userRestriction";
private static final String ENTRIES_SEPARATOR = "|";
@@ -257,9 +260,16 @@
preferenceMetadata.putString(METADATA_HIGHLIGHTABLE_MENU_KEY,
getHighlightableMenuKey(preferenceAttributes));
}
+ if (hasFlag(flags, MetadataFlag.FLAG_NEED_USER_RESTRICTION)) {
+ preferenceMetadata.putString(METADATA_USER_RESTRICTION,
+ getUserRestriction(context, attrs));
+ }
metadata.add(preferenceMetadata);
preferenceAttributes.recycle();
+ if (preferenceScreenAttributes != null) {
+ preferenceScreenAttributes.recycle();
+ }
} while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth));
parser.close();
@@ -351,4 +361,13 @@
return styledAttributes.getBoolean(
R.styleable.Preference_forWork, false);
}
+
+ private static String getUserRestriction(Context context, AttributeSet attrs) {
+ TypedArray preferenceAttributes = context.obtainStyledAttributes(attrs,
+ R.styleable.RestrictedPreference);
+ String userRestriction = preferenceAttributes.getString(
+ R.styleable.RestrictedPreference_userRestriction);
+ preferenceAttributes.recycle();
+ return userRestriction;
+ }
}
\ No newline at end of file
diff --git a/src/com/android/settings/slices/SliceBuilderUtils.java b/src/com/android/settings/slices/SliceBuilderUtils.java
index 6b69540..c9d5f23 100644
--- a/src/com/android/settings/slices/SliceBuilderUtils.java
+++ b/src/com/android/settings/slices/SliceBuilderUtils.java
@@ -28,6 +28,7 @@
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.os.UserHandle;
import android.provider.SettingsSlicesContract;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -50,6 +51,8 @@
import com.android.settings.core.SliderPreferenceController;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.Arrays;
@@ -86,6 +89,16 @@
return buildUnavailableSlice(context, sliceData);
}
+ String userRestriction = sliceData.getUserRestriction();
+ if (!TextUtils.isEmpty(userRestriction)) {
+ RestrictedLockUtils.EnforcedAdmin admin =
+ RestrictedLockUtilsInternal.checkIfRestrictionEnforced(context,
+ userRestriction, UserHandle.myUserId());
+ if (admin != null) {
+ return buildIntentSlice(context, sliceData, controller);
+ }
+ }
+
switch (sliceData.getSliceType()) {
case SliceData.SliceType.INTENT:
return buildIntentSlice(context, sliceData, controller);
diff --git a/src/com/android/settings/slices/SliceData.java b/src/com/android/settings/slices/SliceData.java
index 01b29b2..d97ebf5 100644
--- a/src/com/android/settings/slices/SliceData.java
+++ b/src/com/android/settings/slices/SliceData.java
@@ -73,6 +73,8 @@
private final int mHighlightMenuRes;
+ private final String mUserRestriction;
+
@SliceType
private final int mSliceType;
@@ -132,6 +134,10 @@
return mIsPublicSlice;
}
+ public String getUserRestriction() {
+ return mUserRestriction;
+ }
+
private SliceData(Builder builder) {
mKey = builder.mKey;
mTitle = builder.mTitle;
@@ -146,6 +152,7 @@
mUnavailableSliceSubtitle = builder.mUnavailableSliceSubtitle;
mIsPublicSlice = builder.mIsPublicSlice;
mHighlightMenuRes = builder.mHighlightMenuRes;
+ mUserRestriction = builder.mUserRestriction;
}
@Override
@@ -189,6 +196,8 @@
private boolean mIsPublicSlice;
+ private String mUserRestriction;
+
public Builder setKey(String key) {
mKey = key;
return this;
@@ -255,6 +264,11 @@
return this;
}
+ public Builder setUserRestriction(String userRestriction) {
+ mUserRestriction = userRestriction;
+ return this;
+ }
+
public SliceData build() {
if (TextUtils.isEmpty(mKey)) {
throw new InvalidSliceDataException("Key cannot be empty");
diff --git a/src/com/android/settings/slices/SliceDataConverter.java b/src/com/android/settings/slices/SliceDataConverter.java
index 5177ff7..61165cd 100644
--- a/src/com/android/settings/slices/SliceDataConverter.java
+++ b/src/com/android/settings/slices/SliceDataConverter.java
@@ -22,6 +22,7 @@
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_SUMMARY;
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_TITLE;
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_UNAVAILABLE_SLICE_SUBTITLE;
+import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_USER_RESTRICTION;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.settings.SettingsEnums;
@@ -189,7 +190,8 @@
| MetadataFlag.FLAG_NEED_PREF_TITLE
| MetadataFlag.FLAG_NEED_PREF_ICON
| MetadataFlag.FLAG_NEED_PREF_SUMMARY
- | MetadataFlag.FLAG_UNAVAILABLE_SLICE_SUBTITLE);
+ | MetadataFlag.FLAG_UNAVAILABLE_SLICE_SUBTITLE
+ | MetadataFlag.FLAG_NEED_USER_RESTRICTION);
for (Bundle bundle : metadata) {
// TODO (b/67996923) Non-controller Slices should become intent-only slices.
@@ -218,6 +220,7 @@
METADATA_UNAVAILABLE_SLICE_SUBTITLE);
final boolean isPublicSlice = controller.isPublicSlice();
final int highlightMenuRes = controller.getSliceHighlightMenuRes();
+ final String userRestriction = bundle.getString(METADATA_USER_RESTRICTION);
final SliceData xmlSlice = new SliceData.Builder()
.setKey(key)
@@ -232,6 +235,7 @@
.setUnavailableSliceSubtitle(unavailableSliceSubtitle)
.setIsPublicSlice(isPublicSlice)
.setHighlightMenuRes(highlightMenuRes)
+ .setUserRestriction(userRestriction)
.build();
xmlSliceData.add(xmlSlice);
diff --git a/src/com/android/settings/slices/SlicesDatabaseAccessor.java b/src/com/android/settings/slices/SlicesDatabaseAccessor.java
index 75f0220..93aade1 100644
--- a/src/com/android/settings/slices/SlicesDatabaseAccessor.java
+++ b/src/com/android/settings/slices/SlicesDatabaseAccessor.java
@@ -51,6 +51,7 @@
IndexColumns.SLICE_TYPE,
IndexColumns.UNAVAILABLE_SLICE_SUBTITLE,
IndexColumns.HIGHLIGHT_MENU_RESOURCE,
+ IndexColumns.USER_RESTRICTION,
};
private final Context mContext;
@@ -166,6 +167,8 @@
cursor.getColumnIndex(IndexColumns.UNAVAILABLE_SLICE_SUBTITLE));
final int highlightMenuRes = cursor.getInt(
cursor.getColumnIndex(IndexColumns.HIGHLIGHT_MENU_RESOURCE));
+ final String userRestriction = cursor.getString(
+ cursor.getColumnIndex(IndexColumns.USER_RESTRICTION));
if (isIntentOnly) {
sliceType = SliceData.SliceType.INTENT;
@@ -184,6 +187,7 @@
.setSliceType(sliceType)
.setUnavailableSliceSubtitle(unavailableSliceSubtitle)
.setHighlightMenuRes(highlightMenuRes)
+ .setUserRestriction(userRestriction)
.build();
}
diff --git a/src/com/android/settings/slices/SlicesDatabaseHelper.java b/src/com/android/settings/slices/SlicesDatabaseHelper.java
index 69ad702..cad045e 100644
--- a/src/com/android/settings/slices/SlicesDatabaseHelper.java
+++ b/src/com/android/settings/slices/SlicesDatabaseHelper.java
@@ -36,7 +36,7 @@
private static final String DATABASE_NAME = "slices_index.db";
private static final String SHARED_PREFS_TAG = "slices_shared_prefs";
- private static final int DATABASE_VERSION = 9;
+ private static final int DATABASE_VERSION = 10;
public interface Tables {
String TABLE_SLICES_INDEX = "slices_index";
@@ -108,6 +108,11 @@
* Resource ID for the menu entry of the setting.
*/
String HIGHLIGHT_MENU_RESOURCE = "highlight_menu";
+
+ /**
+ * The name of user restriction for the setting.
+ */
+ String USER_RESTRICTION = "user_restriction";
}
private static final String CREATE_SLICES_TABLE =
@@ -138,6 +143,8 @@
+ IndexColumns.PUBLIC_SLICE
+ ", "
+ IndexColumns.HIGHLIGHT_MENU_RESOURCE
+ + ", "
+ + IndexColumns.USER_RESTRICTION
+ " INTEGER DEFAULT 0 "
+ ");";
diff --git a/src/com/android/settings/slices/SlicesIndexer.java b/src/com/android/settings/slices/SlicesIndexer.java
index 0160843..3e7f800 100644
--- a/src/com/android/settings/slices/SlicesIndexer.java
+++ b/src/com/android/settings/slices/SlicesIndexer.java
@@ -117,6 +117,7 @@
dataRow.getUnavailableSliceSubtitle());
values.put(IndexColumns.PUBLIC_SLICE, dataRow.isPublicSlice());
values.put(IndexColumns.HIGHLIGHT_MENU_RESOURCE, dataRow.getHighlightMenuRes());
+ values.put(IndexColumns.USER_RESTRICTION, dataRow.getUserRestriction());
database.replaceOrThrow(Tables.TABLE_SLICES_INDEX, null /* nullColumnHack */,
values);
diff --git a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
index 45fea57..67a55e6 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
@@ -42,28 +42,33 @@
import com.android.settings.testutils.FakeToggleController;
import com.android.settings.testutils.FakeUnavailablePreferenceController;
import com.android.settings.testutils.SliceTester;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowRestrictedLockUtilsInternal.class)
public class SliceBuilderUtilsTest {
- private final String KEY = "KEY";
- private final String TITLE = "title";
- private final String SUMMARY = "summary";
- private final String SCREEN_TITLE = "screen title";
- private final String KEYWORDS = "a, b, c";
- private final String FRAGMENT_NAME = "fragment name";
- private final int ICON = R.drawable.ic_settings_accent;
- private final Uri URI = Uri.parse("content://com.android.settings.slices/test");
- private final Class TOGGLE_CONTROLLER = FakeToggleController.class;
- private final Class SLIDER_CONTROLLER = FakeSliderController.class;
- private final Class INVALID_SLIDER_CONTROLLER = FakeInvalidSliderController.class;
- private final Class CONTEXT_CONTROLLER = FakeContextOnlyPreferenceController.class;
+ private static final String KEY = "KEY";
+ private static final String TITLE = "title";
+ private static final String SUMMARY = "summary";
+ private static final String SCREEN_TITLE = "screen title";
+ private static final String KEYWORDS = "a, b, c";
+ private static final String FRAGMENT_NAME = "fragment name";
+ private static final String RESTRICTION = "no_brightness";
+ private static final int ICON = R.drawable.ic_settings_accent;
+ private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");
+ private static final Class TOGGLE_CONTROLLER = FakeToggleController.class;
+ private static final Class SLIDER_CONTROLLER = FakeSliderController.class;
+ private static final Class INVALID_SLIDER_CONTROLLER = FakeInvalidSliderController.class;
+ private static final Class CONTEXT_CONTROLLER = FakeContextOnlyPreferenceController.class;
private Context mContext;
@@ -74,6 +79,11 @@
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
}
+ @After
+ public void tearDown() {
+ ShadowRestrictedLockUtilsInternal.reset();
+ }
+
@Test
public void buildIntentSlice_returnsMatchingSlice() {
final SliceData sliceData = getMockData(CONTEXT_CONTROLLER, SliceData.SliceType.INTENT);
@@ -99,6 +109,27 @@
}
@Test
+ public void buildToggleSlice_withUserRestriction_shouldReturnToggleSlice() {
+ final SliceData mockData = getMockData(TOGGLE_CONTROLLER, SliceData.SliceType.SWITCH,
+ RESTRICTION);
+
+ final Slice slice = SliceBuilderUtils.buildSlice(mContext, mockData);
+
+ SliceTester.testSettingsToggleSlice(mContext, slice, mockData);
+ }
+
+ @Test
+ public void buildToggleSlice_withUserRestrictionAndRestricted_shouldReturnIntentSlice() {
+ final SliceData mockData = getMockData(TOGGLE_CONTROLLER, SliceData.SliceType.SWITCH,
+ RESTRICTION);
+ ShadowRestrictedLockUtilsInternal.setRestricted(true);
+
+ final Slice slice = SliceBuilderUtils.buildSlice(mContext, mockData);
+
+ SliceTester.testSettingsIntentSlice(mContext, slice, mockData);
+ }
+
+ @Test
public void testGetPreferenceController_buildsMatchingController() {
final BasePreferenceController controller =
SliceBuilderUtils.getPreferenceController(mContext, getMockData());
@@ -425,8 +456,19 @@
null /* unavailableSliceSubtitle */);
}
+ private SliceData getMockData(Class prefController, int sliceType, String userRestriction) {
+ return getMockData(prefController, SUMMARY, sliceType, SCREEN_TITLE, ICON,
+ null /* unavailableSliceSubtitle */, userRestriction);
+ }
+
private SliceData getMockData(Class prefController, String summary, int sliceType,
String screenTitle, int icon, String unavailableSliceSubtitle) {
+ return getMockData(prefController, summary, sliceType, screenTitle, icon,
+ unavailableSliceSubtitle, null /* userRestriction */);
+ }
+
+ private SliceData getMockData(Class prefController, String summary, int sliceType,
+ String screenTitle, int icon, String unavailableSliceSubtitle, String userRestriction) {
return new SliceData.Builder()
.setKey(KEY)
.setTitle(TITLE)
@@ -439,6 +481,7 @@
.setPreferenceControllerClassName(prefController.getName())
.setSliceType(sliceType)
.setUnavailableSliceSubtitle(unavailableSliceSubtitle)
+ .setUserRestriction(userRestriction)
.build();
}
}