diff options
12 files changed, 884 insertions, 2 deletions
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk index c8606686749d..2189b55357af 100644 --- a/packages/SettingsLib/Android.mk +++ b/packages/SettingsLib/Android.mk @@ -3,9 +3,22 @@ include $(CLEAR_VARS) LOCAL_MODULE := SettingsLib -LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-v4 \ + android-support-v7-recyclerview \ + android-support-v7-preference \ + android-support-v7-appcompat \ + android-support-v14-preference + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \ + frameworks/support/v7/preference/res \ + frameworks/support/v14/preference/res \ + frameworks/support/v7/appcompat/res \ + frameworks/support/v7/recyclerview/res -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_AAPT_FLAGS := --auto-add-overlay \ + --extra-packages android.support.v7.preference:android.support.v14.preference:android.support.v17.preference:android.support.v7.appcompat:android.support.v7.recyclerview + include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml b/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml new file mode 100644 index 000000000000..b3d7cf925ef2 --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="21dp" + android:height="21dp" + android:viewportWidth="21.0" + android:viewportHeight="21.0" + android:tint="?android:attr/colorAccent"> + <path + android:fillColor="@android:color/white" + android:pathData="M8,16c1.1,0,2-0.9,2-2s-0.9-2-2-2s-2,0.9-2,2S6.9,16,8,16zM14,7h-1V5c0-2.8-2.2-5-5-5S3,2.2,3,5v2H2C0.9,7,0,7.9,0,9v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V9C16,7.9,15.1,7,14,7z M4.9,5c0-1.7,1.4-3.1,3.1-3.1s3.1,1.4,3.1,3.1v2H4.9V5z M14,19H2V9h12V19z" /> +</vector> diff --git a/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml b/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml new file mode 100644 index 000000000000..f7a9c9f1816b --- /dev/null +++ b/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/text1" + style="?android:attr/spinnerDropDownItemStyle" + android:singleLine="true" + android:layout_width="wrap_content" + android:layout_height="?android:attr/listPreferredItemHeightSmall" + android:ellipsize="marquee" />
\ No newline at end of file diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml new file mode 100644 index 000000000000..46267a218a3f --- /dev/null +++ b/packages/SettingsLib/res/values/attrs.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <declare-styleable name="RestrictedPreference"> + <attr name="userRestriction" format="string"/> + </declare-styleable> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml new file mode 100644 index 000000000000..c090468c3e8f --- /dev/null +++ b/packages/SettingsLib/res/values/colors.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <color name="disabled_text_color">#66000000</color> <!-- 38% black --> +</resources> diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index d7c78f6ffa6e..9a1d6a404b08 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -31,4 +31,8 @@ <dimen name="user_spinner_padding">4dp</dimen> <dimen name="user_spinner_padding_sides">20dp</dimen> <dimen name="user_spinner_item_height">56dp</dimen> + + <!-- Lock icon for preferences locked by admin --> + <dimen name="restricted_lock_icon_size">16dp</dimen> + <dimen name="restricted_lock_icon_padding">4dp</dimen> </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java new file mode 100644 index 000000000000..c2f885dabe60 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.v7.preference.DropDownPreference; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + +public class RestrictedDropDownPreference extends DropDownPreference { + private Spinner mSpinner; + private final Drawable mRestrictedPadlock; + private final int mRestrictedPadlockPadding; + private List<RestrictedItem> mRestrictedItems = new ArrayList<>(); + + public RestrictedDropDownPreference(Context context) { + this(context, null); + } + + public RestrictedDropDownPreference(Context context, AttributeSet attrs) { + super(context, attrs); + + mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(context); + mRestrictedPadlockPadding = context.getResources().getDimensionPixelSize( + R.dimen.restricted_lock_icon_padding); + } + + private final OnItemSelectedListener mItemSelectedListener = new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View v, int position, long id) { + if (position >= 0) { + String value = getEntryValues()[position].toString(); + RestrictedItem item = getRestrictedItemForEntryValue(value); + if (item != null) { + RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), + item.enforcedAdmin); + mSpinner.setSelection(findIndexOfValue(getValue())); + } else if (!value.equals(getValue()) && callChangeListener(value)) { + setValue(value); + } + } + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + // noop + } + }; + + @Override + protected ArrayAdapter createAdapter() { + return new RestrictedArrayItemAdapter(getContext()); + } + + @Override + public void setValue(String value) { + if (getRestrictedItemForEntryValue(value) != null) { + return; + } + super.setValue(value); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder view) { + super.onBindViewHolder(view); + mSpinner = (Spinner) view.itemView.findViewById(R.id.spinner); + mSpinner.setOnItemSelectedListener(mItemSelectedListener); + } + + private class RestrictedArrayItemAdapter extends ArrayAdapter<String> { + public RestrictedArrayItemAdapter(Context context) { + super(context, R.layout.spinner_dropdown_restricted_item); + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + TextView view = (TextView) super.getView(position, convertView, parent); + CharSequence entry = getItem(position); + boolean isEntryRestricted = isRestrictedForEntry(entry); + RestrictedLockUtils.setTextViewPadlock(getContext(), view, isEntryRestricted); + view.setEnabled(!isEntryRestricted); + return view; + } + } + + private boolean isRestrictedForEntry(CharSequence entry) { + if (entry == null) { + return false; + } + for (RestrictedItem item : mRestrictedItems) { + if (entry.equals(item.entry)) { + return true; + } + } + return false; + } + + private RestrictedItem getRestrictedItemForEntryValue(CharSequence entryValue) { + if (entryValue == null) { + return null; + } + for (RestrictedItem item : mRestrictedItems) { + if (entryValue.equals(item.entryValue)) { + return item; + } + } + return null; + } + + public void addRestrictedItem(RestrictedItem item) { + mRestrictedItems.add(item); + } + + public static class RestrictedItem { + public CharSequence entry; + public CharSequence entryValue; + public EnforcedAdmin enforcedAdmin; + + public RestrictedItem(CharSequence entry, CharSequence entryValue, + EnforcedAdmin enforcedAdmin) { + this.entry = entry; + this.entryValue = entryValue; + this.enforcedAdmin = enforcedAdmin; + } + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java new file mode 100644 index 000000000000..e63130de6f4e --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.text.style.ImageSpan; + +/** + * An extension of ImageSpan which adds a padding before the image. + */ +public class RestrictedLockImageSpan extends ImageSpan { + private Context mContext; + private final float mExtraPadding; + private final Drawable mRestrictedPadlock; + + public RestrictedLockImageSpan(Context context) { + // we are overriding getDrawable, so passing null to super class here. + super((Drawable) null); + + mContext = context; + mExtraPadding = mContext.getResources().getDimensionPixelSize( + R.dimen.restricted_lock_icon_padding); + mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext); + } + + @Override + public Drawable getDrawable() { + return mRestrictedPadlock; + } + + @Override + public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, + int bottom, Paint paint) { + Drawable drawable = getDrawable(); + canvas.save(); + + // Add extra padding before the padlock. + float transX = x + mExtraPadding; + float transY = bottom - drawable.getBounds().bottom - paint.getFontMetricsInt().descent; + + canvas.translate(transX, transY); + drawable.draw(canvas); + canvas.restore(); + } + + @Override + public int getSize(Paint paint, CharSequence text, int start, int end, + Paint.FontMetricsInt fontMetrics) { + int size = super.getSize(paint, text, start, end, fontMetrics); + size += 2 * mExtraPadding; + return size; + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java new file mode 100644 index 000000000000..f6caaa9642c4 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib; + +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.Spanned; +import android.text.SpannableStringBuilder; +import android.text.style.ForegroundColorSpan; +import android.text.style.ImageSpan; +import android.view.MenuItem; +import android.widget.TextView; + +import java.util.List; + +/** + * Utility class to host methods usable in adding a restricted padlock icon and showing admin + * support message dialog. + */ +public class RestrictedLockUtils { + /** + * @return drawables for displaying with settings that are locked by a device admin. + */ + public static Drawable getRestrictedPadlock(Context context) { + Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_settings_lock_outline); + final int iconSize = context.getResources().getDimensionPixelSize( + R.dimen.restricted_lock_icon_size); + restrictedPadlock.setBounds(0, 0, iconSize, iconSize); + return restrictedPadlock; + } + + /** + * Checks if a restriction is enforced on a user and returns the enforced admin and + * admin userId. + * + * @param userRestriction Restriction to check + * @param userId User which we need to check if restriction is enforced on. + * @return EnforcedAdmin Object containing the enforce admin and admin user details, or + * {@code null} If the restriction is not set. If the restriction is set by both device owner + * and profile owner, then the admin will be set to {@code null} and userId to + * {@link UserHandle#USER_NULL}. + */ + public static EnforcedAdmin checkIfRestrictionEnforced(Context context, + String userRestriction, int userId) { + DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( + Context.DEVICE_POLICY_SERVICE); + ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser(); + int deviceOwnerUserId = dpm.getDeviceOwnerUserId(); + boolean enforcedByDeviceOwner = false; + if (deviceOwner != null && deviceOwnerUserId != UserHandle.USER_NULL) { + Bundle enforcedRestrictions = dpm.getUserRestrictions(deviceOwner, deviceOwnerUserId); + if (enforcedRestrictions != null + && enforcedRestrictions.getBoolean(userRestriction, false)) { + enforcedByDeviceOwner = true; + } + } + + ComponentName profileOwner = null; + boolean enforcedByProfileOwner = false; + if (userId != UserHandle.USER_NULL) { + profileOwner = dpm.getProfileOwnerAsUser(userId); + if (profileOwner != null) { + Bundle enforcedRestrictions = dpm.getUserRestrictions(profileOwner, userId); + if (enforcedRestrictions != null + && enforcedRestrictions.getBoolean(userRestriction, false)) { + enforcedByProfileOwner = true; + } + } + } + + if (!enforcedByDeviceOwner && !enforcedByProfileOwner) { + return null; + } + + EnforcedAdmin admin = null; + if (enforcedByDeviceOwner && enforcedByProfileOwner) { + admin = new EnforcedAdmin(); + } else if (enforcedByDeviceOwner) { + admin = new EnforcedAdmin(deviceOwner, deviceOwnerUserId); + } else { + admin = new EnforcedAdmin(profileOwner, userId); + } + return admin; + } + + /** + * Checks if lock screen notification features are disabled by policy. This should be + * only used for keyguard notification features but not the keyguard features + * (e.g. KEYGUARD_DISABLE_FINGERPRINT) where a profile owner can set them on the parent user + * as it won't work for that case. + * + * @param keyguardNotificationFeatures Could be any of notification features that can be + * disabled by {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}. + * @return EnforcedAdmin Object containing the enforce admin and admin user details, or + * {@code null} If the notification features are not disabled. If the restriction is set by + * multiple admins, then the admin will be set to {@code null} and userId to + * {@link UserHandle#USER_NULL}. + */ + public static EnforcedAdmin checkIfKeyguardNotificationFeaturesDisabled(Context context, + int keyguardNotificationFeatures) { + final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( + Context.DEVICE_POLICY_SERVICE); + boolean isDisabledByMultipleAdmins = false; + ComponentName adminComponent = null; + List<ComponentName> admins = dpm.getActiveAdmins(); + int disabledKeyguardFeatures; + for (ComponentName admin : admins) { + disabledKeyguardFeatures = dpm.getKeyguardDisabledFeatures(admin); + if ((disabledKeyguardFeatures & keyguardNotificationFeatures) != 0) { + if (adminComponent == null) { + adminComponent = admin; + } else { + isDisabledByMultipleAdmins = true; + break; + } + } + } + EnforcedAdmin enforcedAdmin = null; + if (adminComponent != null) { + if (!isDisabledByMultipleAdmins) { + enforcedAdmin = new EnforcedAdmin(adminComponent, UserHandle.myUserId()); + } else { + enforcedAdmin = new EnforcedAdmin(); + } + } + return enforcedAdmin; + } + + /** + * Set the menu item as disabled by admin by adding a restricted padlock at the end of the + * text and set the click listener which will send an intent to show the admin support details + * dialog. + */ + public static void setMenuItemAsDisabledByAdmin(final Context context, + final MenuItem item, final EnforcedAdmin admin) { + SpannableStringBuilder sb = new SpannableStringBuilder(item.getTitle()); + removeExistingRestrictedSpans(sb); + + final int disabledColor = context.getColor(R.color.disabled_text_color); + sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ImageSpan image = new RestrictedLockImageSpan(context); + sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + item.setTitle(sb); + + item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + sendShowAdminSupportDetailsIntent(context, admin); + return true; + } + }); + } + + private static void removeExistingRestrictedSpans(SpannableStringBuilder sb) { + final int length = sb.length(); + RestrictedLockImageSpan[] imageSpans = sb.getSpans(length - 1, length, + RestrictedLockImageSpan.class); + for (ImageSpan span : imageSpans) { + sb.removeSpan(span); + } + ForegroundColorSpan[] colorSpans = sb.getSpans(0, length, ForegroundColorSpan.class); + for (ForegroundColorSpan span : colorSpans) { + sb.removeSpan(span); + } + } + + /** + * Send the intent to trigger the {@link android.settings.ShowAdminSupportDetailsDialog}. + */ + public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) { + Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS); + int adminUserId = UserHandle.myUserId(); + if (admin != null) { + if (admin.component != null) { + intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin.component); + } + if (admin.userId != UserHandle.USER_NULL) { + adminUserId = admin.userId; + } + intent.putExtra(Intent.EXTRA_USER_ID, adminUserId); + } + context.startActivityAsUser(intent, new UserHandle(adminUserId)); + } + + public static void setTextViewPadlock(Context context, + TextView textView, boolean showPadlock) { + final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText()); + removeExistingRestrictedSpans(sb); + if (showPadlock) { + final ImageSpan image = new RestrictedLockImageSpan(context); + sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + textView.setText(sb); + } + + public static class EnforcedAdmin { + public ComponentName component = null; + public int userId = UserHandle.USER_NULL; + + public EnforcedAdmin(ComponentName component, int userId) { + this.component = component; + this.userId = userId; + } + + public EnforcedAdmin() {} + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java new file mode 100644 index 000000000000..569017a6af36 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settingslib; + +import android.content.Context; +import android.os.UserHandle; +import android.support.v4.content.res.TypedArrayUtils; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceManager; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; + +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + +/** + * Preference class that supports being disabled by a user restriction + * set by a device admin. + */ +public class RestrictedPreference extends Preference { + RestrictedPreferenceHelper mHelper; + + public RestrictedPreference(Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mHelper = new RestrictedPreferenceHelper(context, this, attrs); + } + + public RestrictedPreference(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public RestrictedPreference(Context context, AttributeSet attrs) { + this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle, + android.R.attr.preferenceStyle)); + } + + public RestrictedPreference(Context context) { + this(context, null); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + mHelper.onBindViewHolder(holder); + } + + @Override + public void performClick() { + if (!mHelper.performClick()) { + super.performClick(); + } + } + + @Override + protected void onAttachedToHierarchy(PreferenceManager preferenceManager) { + mHelper.onAttachedToHierarchy(); + super.onAttachedToHierarchy(preferenceManager); + } + + public void checkRestrictionAndSetDisabled(String userRestriction) { + mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId()); + } + + public void checkRestrictionAndSetDisabled(String userRestriction, int userId) { + mHelper.checkRestrictionAndSetDisabled(userRestriction, userId); + } + + public void setDisabledByAdmin(EnforcedAdmin admin) { + if (mHelper.setDisabledByAdmin(admin)) { + notifyChanged(); + } + } + + public boolean isDisabledByAdmin() { + return mHelper.isDisabledByAdmin(); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java new file mode 100644 index 000000000000..f04150460309 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settingslib; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceViewHolder; +import android.text.Spanned; +import android.text.SpannableStringBuilder; +import android.text.style.ImageSpan; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.widget.TextView; + +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + +/** + * Helper class for managing settings preferences that can be disabled + * by device admins via user restrictions. + */ +public class RestrictedPreferenceHelper { + private final Context mContext; + private final Preference mPreference; + private final Drawable mRestrictedPadlock; + private final int mRestrictedPadlockPadding; + + private boolean mDisabledByAdmin; + private EnforcedAdmin mEnforcedAdmin; + private String mAttrUserRestriction = null; + + RestrictedPreferenceHelper(Context context, Preference preference, + AttributeSet attrs) { + mContext = context; + mPreference = preference; + + mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext); + mRestrictedPadlockPadding = mContext.getResources().getDimensionPixelSize( + R.dimen.restricted_lock_icon_padding); + + mAttrUserRestriction = attrs.getAttributeValue( + R.styleable.RestrictedPreference_userRestriction); + final TypedArray attributes = context.obtainStyledAttributes(attrs, + R.styleable.RestrictedPreference); + final TypedValue userRestriction = + attributes.peekValue(R.styleable.RestrictedPreference_userRestriction); + CharSequence data = null; + if (userRestriction != null && userRestriction.type == TypedValue.TYPE_STRING) { + if (userRestriction.resourceId != 0) { + data = context.getText(userRestriction.resourceId); + } else { + data = userRestriction.string; + } + } + mAttrUserRestriction = data == null ? null : data.toString(); + } + + /** + * Modify PreferenceViewHolder to add padlock if restriction is disabled. + */ + public void onBindViewHolder(PreferenceViewHolder holder) { + final TextView titleView = (TextView) holder.findViewById(android.R.id.title); + if (titleView != null) { + RestrictedLockUtils.setTextViewPadlock(mContext, titleView, mDisabledByAdmin); + if (mDisabledByAdmin) { + holder.itemView.setEnabled(true); + } + } + } + + /** + * Check if the preference is disabled if so handle the click by informing the user. + * + * @return true if the method handled the click. + */ + public boolean performClick() { + if (mDisabledByAdmin) { + RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin); + return true; + } + return false; + } + + /** + * Disable / enable if we have been passed the restriction in the xml. + */ + protected void onAttachedToHierarchy() { + if (mAttrUserRestriction != null) { + checkRestrictionAndSetDisabled(mAttrUserRestriction, UserHandle.myUserId()); + } + } + + /** + * Set the user restriction that is used to disable this preference. + * + * @param userRestriction constant from {@link android.os.UserManager} + * @param userId user to check the restriction for. + */ + public void checkRestrictionAndSetDisabled(String userRestriction, int userId) { + EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext, + userRestriction, userId); + setDisabledByAdmin(admin); + } + + /** + * Disable this preference based on the enforce admin. + * + * @param EnforcedAdmin Details of the admin who enforced the restriction. If it + * is {@code null}, then this preference will be enabled. Otherwise, it will be disabled. + * @return true if the disabled state was changed. + */ + public boolean setDisabledByAdmin(EnforcedAdmin admin) { + final boolean disabled = (admin != null ? true : false); + mEnforcedAdmin = (disabled ? admin : null); + if (mDisabledByAdmin != disabled) { + mDisabledByAdmin = disabled; + mPreference.setEnabled(!disabled); + return true; + } + return false; + } + + public boolean isDisabledByAdmin() { + return mDisabledByAdmin; + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java new file mode 100644 index 000000000000..308477b0c84b --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settingslib; + +import android.content.Context; +import android.os.UserHandle; +import android.support.v4.content.res.TypedArrayUtils; +import android.support.v7.preference.PreferenceManager; +import android.support.v7.preference.PreferenceViewHolder; +import android.support.v14.preference.SwitchPreference; +import android.util.AttributeSet; + +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + +/** + * Version of SwitchPreference that can be disabled by a device admin + * using a user restriction. + */ +public class RestrictedSwitchPreference extends SwitchPreference { + RestrictedPreferenceHelper mHelper; + + public RestrictedSwitchPreference(Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mHelper = new RestrictedPreferenceHelper(context, this, attrs); + } + + public RestrictedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public RestrictedSwitchPreference(Context context, AttributeSet attrs) { + this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.switchPreferenceStyle, + android.R.attr.switchPreferenceStyle)); + } + + public RestrictedSwitchPreference(Context context) { + this(context, null); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + mHelper.onBindViewHolder(holder); + } + + @Override + public void performClick() { + if (!mHelper.performClick()) { + super.performClick(); + } + } + + @Override + protected void onAttachedToHierarchy(PreferenceManager preferenceManager) { + mHelper.onAttachedToHierarchy(); + super.onAttachedToHierarchy(preferenceManager); + } + + public void checkRestrictionAndSetDisabled(String userRestriction) { + mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId()); + } + + public void checkRestrictionAndSetDisabled(String userRestriction, int userId) { + mHelper.checkRestrictionAndSetDisabled(userRestriction, userId); + } + + public void setDisabledByAdmin(EnforcedAdmin admin) { + if (mHelper.setDisabledByAdmin(admin)) { + notifyChanged(); + } + } + + public boolean isDisabledByAdmin() { + return mHelper.isDisabledByAdmin(); + } +} |