diff --git a/res/layout/restricted_dialog_singlechoice.xml b/res/layout/restricted_dialog_singlechoice.xml new file mode 100644 index 00000000000..e5e449b1067 --- /dev/null +++ b/res/layout/restricted_dialog_singlechoice.xml @@ -0,0 +1,44 @@ + + + + + + + \ No newline at end of file diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml index 0456ace86a8..1780765713d 100644 --- a/res/xml/display_settings.xml +++ b/res/xml/display_settings.xml @@ -49,7 +49,7 @@ settings:keywords="@string/keywords_display_wallpaper" android:fragment="com.android.settings.WallpaperTypeSettings" /> - - - - = timeout) { best = i; } } - summary = preference.getContext().getString(R.string.screen_timeout_summary, - entries[best]); + summary = getString(R.string.screen_timeout_summary, entries[best]); } } preference.setSummary(summary); } - private void disableUnusableTimeouts(ListPreference screenTimeoutPreference) { - final DevicePolicyManager dpm = - (DevicePolicyManager) getActivity().getSystemService( - Context.DEVICE_POLICY_SERVICE); - final long maxTimeout = dpm != null ? dpm.getMaximumTimeToLock(null) : 0; - if (maxTimeout == 0) { + private void disableUnusableTimeouts(RestrictedListPreference screenTimeoutPreference) { + final EnforcedAdmin admin = RestrictedLockUtils.checkIfMaximumTimeToLockIsSet( + getActivity()); + if (admin == null) { return; // policy not enforced } + + final DevicePolicyManager dpm = (DevicePolicyManager) getActivity().getSystemService( + Context.DEVICE_POLICY_SERVICE); + if (dpm == null) { + return; + } + final long maxTimeout = dpm.getMaximumTimeToLock(null); final CharSequence[] entries = screenTimeoutPreference.getEntries(); final CharSequence[] values = screenTimeoutPreference.getEntryValues(); - ArrayList revisedEntries = new ArrayList(); - ArrayList revisedValues = new ArrayList(); + long maxTimeoutSelectable = 0; + int maxTimeoutEntryIndex = -1; for (int i = 0; i < values.length; i++) { long timeout = Long.parseLong(values[i].toString()); - if (timeout <= maxTimeout) { - revisedEntries.add(entries[i]); - revisedValues.add(values[i]); + if (timeout > maxTimeout) { + break; } + maxTimeoutSelectable = timeout; + maxTimeoutEntryIndex = i; } - if (revisedEntries.size() != entries.length || revisedValues.size() != values.length) { - final int userPreference = Integer.parseInt(screenTimeoutPreference.getValue()); - screenTimeoutPreference.setEntries( - revisedEntries.toArray(new CharSequence[revisedEntries.size()])); - screenTimeoutPreference.setEntryValues( - revisedValues.toArray(new CharSequence[revisedValues.size()])); - if (userPreference <= maxTimeout) { - screenTimeoutPreference.setValue(String.valueOf(userPreference)); - } else if (revisedValues.size() > 0 - && Long.parseLong(revisedValues.get(revisedValues.size() - 1).toString()) - == maxTimeout) { - // If the last one happens to be the same as the max timeout, select that - screenTimeoutPreference.setValue(String.valueOf(maxTimeout)); - } else { - // There will be no highlighted selection since nothing in the list matches - // maxTimeout. The user can still select anything less than maxTimeout. - // TODO: maybe append maxTimeout to the list and mark selected. - } + // If there are no possible options for the user, then set this preference as disabled + // by admin, otherwise remove the padlock in case it was set earlier. + if (maxTimeoutSelectable == 0) { + screenTimeoutPreference.setDisabledByAdmin(admin); + return; + } else { + screenTimeoutPreference.setDisabledByAdmin(null); + } + + screenTimeoutPreference.clearRestrictedItems(); + // Set all the entries after the maximum selectable timeout as disabled by admin. + for (int i = maxTimeoutEntryIndex + 1; i < values.length; i++) { + screenTimeoutPreference.addRestrictedItem( + new RestrictedItem(entries[i], values[i], admin)); + } + + final int userPreference = Integer.parseInt(screenTimeoutPreference.getValue()); + if (userPreference <= maxTimeout) { + screenTimeoutPreference.setValue(String.valueOf(userPreference)); + } else if (maxTimeoutSelectable == maxTimeout) { + screenTimeoutPreference.setValue(String.valueOf(maxTimeout)); + } else { + // There will be no highlighted selection since nothing in the list matches + // maxTimeout. The user can still select anything less than maxTimeout. + // TODO: maybe append maxTimeout to the list and mark selected. } - screenTimeoutPreference.setEnabled(revisedEntries.size() > 0); } int floatToIndex(float val) { @@ -358,6 +373,13 @@ public class DisplaySettings extends SettingsPreferenceFragment implements public void onResume() { super.onResume(); updateState(); + + final long currentTimeout = Settings.System.getLong(getActivity().getContentResolver(), + SCREEN_OFF_TIMEOUT, FALLBACK_SCREEN_TIMEOUT_VALUE); + mScreenTimeoutPreference.setValue(String.valueOf(currentTimeout)); + mScreenTimeoutPreference.setOnPreferenceChangeListener(this); + disableUnusableTimeouts(mScreenTimeoutPreference); + updateTimeoutPreferenceDescription(currentTimeout); } private void updateState() { diff --git a/src/com/android/settings/RestrictedListPreference.java b/src/com/android/settings/RestrictedListPreference.java new file mode 100644 index 00000000000..35ee30d10c0 --- /dev/null +++ b/src/com/android/settings/RestrictedListPreference.java @@ -0,0 +1,227 @@ +/* + * 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.settings; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v14.preference.ListPreferenceDialogFragment; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.CheckedTextView; +import android.widget.ImageView; +import android.widget.ListAdapter; + +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.RestrictedPreferenceHelper; + +import java.util.ArrayList; +import java.util.List; + +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + +public class RestrictedListPreference extends CustomListPreference { + private final RestrictedPreferenceHelper mHelper; + private final List mRestrictedItems = new ArrayList<>(); + + public RestrictedListPreference(Context context, AttributeSet attrs) { + super(context, attrs); + mHelper = new RestrictedPreferenceHelper(context, this, attrs); + } + + public RestrictedListPreference(Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mHelper = new RestrictedPreferenceHelper(context, this, attrs); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + mHelper.onBindViewHolder(holder); + } + + @Override + public void performClick() { + if (!mHelper.performClick()) { + super.performClick(); + } + } + + @Override + public void setEnabled(boolean enabled) { + if (enabled && isDisabledByAdmin()) { + mHelper.setDisabledByAdmin(null); + return; + } + super.setEnabled(enabled); + } + + public void setDisabledByAdmin(EnforcedAdmin admin) { + if (mHelper.setDisabledByAdmin(admin)) { + notifyChanged(); + } + } + + public boolean isDisabledByAdmin() { + return mHelper.isDisabledByAdmin(); + } + + public boolean isRestrictedForEntry(CharSequence entry) { + if (entry == null) { + return false; + } + for (RestrictedItem item : mRestrictedItems) { + if (entry.equals(item.entry)) { + return true; + } + } + return false; + } + + public void addRestrictedItem(RestrictedItem item) { + mRestrictedItems.add(item); + } + + public void clearRestrictedItems() { + mRestrictedItems.clear(); + } + + private RestrictedItem getRestrictedItemForEntryValue(CharSequence entryValue) { + if (entryValue == null) { + return null; + } + for (RestrictedItem item : mRestrictedItems) { + if (entryValue.equals(item.entryValue)) { + return item; + } + } + return null; + } + + protected ListAdapter createListAdapter() { + final String selectedValue = getValue(); + final int selectedIndex = + (selectedValue == null) ? -1 : findIndexOfValue(selectedValue); + return new RestrictedArrayAdapter(getContext(), getEntries(), selectedIndex); + } + + @Override + protected void onPrepareDialogBuilder(AlertDialog.Builder builder, + DialogInterface.OnClickListener listener) { + builder.setAdapter(createListAdapter(), listener); + } + + + public class RestrictedArrayAdapter extends ArrayAdapter { + private final int mSelectedIndex; + public RestrictedArrayAdapter(Context context, CharSequence[] objects, int selectedIndex) { + super(context, R.layout.restricted_dialog_singlechoice, R.id.text1, objects); + mSelectedIndex = selectedIndex; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View root = super.getView(position, convertView, parent); + CharSequence entry = getItem(position); + CheckedTextView text = (CheckedTextView) root.findViewById(R.id.text1); + ImageView padlock = (ImageView) root.findViewById(R.id.restricted_lock_icon); + if (isRestrictedForEntry(entry)) { + text.setEnabled(false); + padlock.setVisibility(View.VISIBLE); + } else { + if (position == mSelectedIndex) { + text.setChecked(true); + } + text.setEnabled(true); + padlock.setVisibility(View.GONE); + } + return root; + } + + @Override + public boolean hasStableIds() { + return true; + } + + @Override + public long getItemId(int position) { + return position; + } + } + + public static class RestrictedListPreferenceDialogFragment extends + CustomListPreference.CustomListPreferenceDialogFragment { + public static ListPreferenceDialogFragment newInstance(String key) { + final ListPreferenceDialogFragment fragment + = new RestrictedListPreferenceDialogFragment(); + final Bundle b = new Bundle(1); + b.putString(ARG_KEY, key); + fragment.setArguments(b); + return fragment; + } + + private RestrictedListPreference getCustomizablePreference() { + return (RestrictedListPreference) getPreference(); + } + + @Override + protected DialogInterface.OnClickListener getOnItemClickListener() { + return new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + final RestrictedListPreference preference = getCustomizablePreference(); + if (which < 0 || which >= preference.getEntryValues().length) { + return; + } + String entryValue = preference.getEntryValues()[which].toString(); + RestrictedItem item = preference.getRestrictedItemForEntryValue(entryValue); + if (item != null) { + RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), + item.enforcedAdmin); + } else { + setClickedDialogEntryIndex(which); + } + + /* + * Clicking on an item simulates the positive button + * click, and dismisses the dialog. + */ + RestrictedListPreferenceDialogFragment.this.onClick(dialog, + DialogInterface.BUTTON_POSITIVE); + dialog.dismiss(); + } + }; + } + } + + public static class RestrictedItem { + public final CharSequence entry; + public final CharSequence entryValue; + public final 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/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java index d39203d6250..17831363b46 100644 --- a/src/com/android/settings/SecuritySettings.java +++ b/src/com/android/settings/SecuritySettings.java @@ -39,7 +39,6 @@ import android.provider.Settings; import android.security.KeyStore; import android.service.trust.TrustAgentService; import android.support.v14.preference.SwitchPreference; -import android.support.v7.preference.ListPreference; import android.support.v7.preference.Preference; import android.support.v7.preference.Preference.OnPreferenceChangeListener; import android.support.v7.preference.Preference.OnPreferenceClickListener; @@ -54,6 +53,7 @@ import android.util.Log; import com.android.internal.logging.MetricsLogger; import com.android.internal.widget.LockPatternUtils; +import com.android.settings.RestrictedListPreference; import com.android.settings.TrustAgentUtils.TrustAgentComponentInfo; import com.android.settings.fingerprint.FingerprintEnrollIntroduction; import com.android.settings.fingerprint.FingerprintSettings; @@ -61,6 +61,7 @@ import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Index; import com.android.settings.search.Indexable; import com.android.settings.search.SearchIndexableRaw; +import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedPreference; import com.android.settingslib.RestrictedSwitchPreference; @@ -69,6 +70,9 @@ import java.util.List; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; +import static com.android.settings.RestrictedListPreference.RestrictedItem; +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + /** * Gesture lock pattern settings. */ @@ -124,7 +128,7 @@ public class SecuritySettings extends SettingsPreferenceFragment private ChooseLockSettingsHelper mChooseLockSettingsHelper; private LockPatternUtils mLockPatternUtils; - private ListPreference mLockAfter; + private RestrictedListPreference mLockAfter; private SwitchPreference mVisiblePattern; @@ -267,7 +271,7 @@ public class SecuritySettings extends SettingsPreferenceFragment } // lock after preference - mLockAfter = (ListPreference) root.findPreference(KEY_LOCK_AFTER_TIMEOUT); + mLockAfter = (RestrictedListPreference) root.findPreference(KEY_LOCK_AFTER_TIMEOUT); if (mLockAfter != null) { setupLockAfterPreference(); updateLockAfterPreferenceSummary(); @@ -552,72 +556,97 @@ public class SecuritySettings extends SettingsPreferenceFragment Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, 5000); mLockAfter.setValue(String.valueOf(currentTimeout)); mLockAfter.setOnPreferenceChangeListener(this); - final long adminTimeout = (mDPM != null ? mDPM.getMaximumTimeToLock(null) : 0); - final long displayTimeout = Math.max(0, - Settings.System.getInt(getContentResolver(), SCREEN_OFF_TIMEOUT, 0)); - if (adminTimeout > 0) { - // This setting is a slave to display timeout when a device policy is enforced. - // As such, maxLockTimeout = adminTimeout - displayTimeout. - // If there isn't enough time, shows "immediately" setting. - disableUnusableTimeouts(Math.max(0, adminTimeout - displayTimeout)); + final EnforcedAdmin admin = RestrictedLockUtils.checkIfMaximumTimeToLockIsSet( + getActivity()); + if (admin != null) { + final long adminTimeout = (mDPM != null ? mDPM.getMaximumTimeToLock(null) : 0); + final long displayTimeout = Math.max(0, + Settings.System.getInt(getContentResolver(), SCREEN_OFF_TIMEOUT, 0)); + if (adminTimeout > 0) { + // This setting is a slave to display timeout when a device policy is enforced. + // As such, maxLockTimeout = adminTimeout - displayTimeout. + // If there isn't enough time, shows "immediately" setting. + disableUnusableTimeouts(Math.max(0, adminTimeout - displayTimeout), admin); + } } } private void updateLockAfterPreferenceSummary() { - // Update summary message with current value - long currentTimeout = Settings.Secure.getLong(getContentResolver(), - Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, 5000); - final CharSequence[] entries = mLockAfter.getEntries(); - final CharSequence[] values = mLockAfter.getEntryValues(); - int best = 0; - for (int i = 0; i < values.length; i++) { - long timeout = Long.valueOf(values[i].toString()); - if (currentTimeout >= timeout) { - best = i; - } - } - - Preference preference = getPreferenceScreen().findPreference(KEY_TRUST_AGENT); - if (preference != null && preference.getTitle().length() > 0) { - if (Long.valueOf(values[best].toString()) == 0) { - mLockAfter.setSummary(getString(R.string.lock_immediately_summary_with_exception, - preference.getTitle())); - } else { - mLockAfter.setSummary(getString(R.string.lock_after_timeout_summary_with_exception, - entries[best], preference.getTitle())); - } + final String summary; + if (mLockAfter.isDisabledByAdmin()) { + summary = getString(R.string.disabled_by_policy_title); } else { - mLockAfter.setSummary(getString(R.string.lock_after_timeout_summary, entries[best])); + // Update summary message with current value + long currentTimeout = Settings.Secure.getLong(getContentResolver(), + Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, 5000); + final CharSequence[] entries = mLockAfter.getEntries(); + final CharSequence[] values = mLockAfter.getEntryValues(); + int best = 0; + for (int i = 0; i < values.length; i++) { + if (mLockAfter.isRestrictedForEntry(entries[i])) { + break; + } + long timeout = Long.valueOf(values[i].toString()); + if (currentTimeout >= timeout) { + best = i; + } + } + + Preference preference = getPreferenceScreen().findPreference(KEY_TRUST_AGENT); + if (preference != null && preference.getTitle().length() > 0) { + if (Long.valueOf(values[best].toString()) == 0) { + summary = getString(R.string.lock_immediately_summary_with_exception, + preference.getTitle()); + } else { + summary = getString(R.string.lock_after_timeout_summary_with_exception, + entries[best], preference.getTitle()); + } + } else { + summary = getString(R.string.lock_after_timeout_summary, entries[best]); + } } + mLockAfter.setSummary(summary); } - private void disableUnusableTimeouts(long maxTimeout) { + private void disableUnusableTimeouts(long maxTimeout, EnforcedAdmin admin) { final CharSequence[] entries = mLockAfter.getEntries(); final CharSequence[] values = mLockAfter.getEntryValues(); - ArrayList revisedEntries = new ArrayList(); - ArrayList revisedValues = new ArrayList(); + long maxTimeoutSelectable = 0; + int maxTimeoutEntryIndex = -1; for (int i = 0; i < values.length; i++) { - long timeout = Long.valueOf(values[i].toString()); - if (timeout <= maxTimeout) { - revisedEntries.add(entries[i]); - revisedValues.add(values[i]); + long timeout = Long.parseLong(values[i].toString()); + if (timeout > maxTimeout) { + break; } + maxTimeoutSelectable = timeout; + maxTimeoutEntryIndex = i; } - if (revisedEntries.size() != entries.length || revisedValues.size() != values.length) { - mLockAfter.setEntries( - revisedEntries.toArray(new CharSequence[revisedEntries.size()])); - mLockAfter.setEntryValues( - revisedValues.toArray(new CharSequence[revisedValues.size()])); - final int userPreference = Integer.valueOf(mLockAfter.getValue()); - if (userPreference <= maxTimeout) { - mLockAfter.setValue(String.valueOf(userPreference)); - } else { - // There will be no highlighted selection since nothing in the list matches - // maxTimeout. The user can still select anything less than maxTimeout. - // TODO: maybe append maxTimeout to the list and mark selected. - } + // If there are no possible options for the user, then set this preference as + // disabled by admin, otherwise remove the padlock in case it was set earlier. + if (maxTimeoutSelectable == 0) { + mLockAfter.setDisabledByAdmin(admin); + return; + } else { + mLockAfter.setDisabledByAdmin(null); + } + + mLockAfter.clearRestrictedItems(); + // Set all the entries after the maximum selectable timeout as disabled by admin. + for (int i = maxTimeoutEntryIndex + 1; i < values.length; i++) { + mLockAfter.addRestrictedItem( + new RestrictedItem(entries[i], values[i], admin)); + } + + final int userPreference = Integer.valueOf(mLockAfter.getValue()); + if (userPreference <= maxTimeout) { + mLockAfter.setValue(String.valueOf(userPreference)); + } else if (maxTimeoutSelectable == maxTimeout) { + mLockAfter.setValue(String.valueOf(maxTimeout)); + } else { + // There will be no highlighted selection since nothing in the list matches + // maxTimeout. The user can still select anything less than maxTimeout. + // TODO: maybe append maxTimeout to the list and mark selected. } - mLockAfter.setEnabled(revisedEntries.size() > 0); } @Override @@ -651,7 +680,7 @@ public class SecuritySettings extends SettingsPreferenceFragment Settings.System.TEXT_SHOW_PASSWORD, 1) != 0); } - if (mResetCredentials != null && !mResetCredentials.isDisabledByAdmin()) { + if (mResetCredentials != null) { mResetCredentials.setEnabled(!mKeyStore.isEmpty()); } diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java index d8b9b9195ed..cb3c151c351 100644 --- a/src/com/android/settings/SettingsPreferenceFragment.java +++ b/src/com/android/settings/SettingsPreferenceFragment.java @@ -476,7 +476,10 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF preference.setKey(UUID.randomUUID().toString()); } DialogFragment f = null; - if (preference instanceof CustomListPreference) { + if (preference instanceof RestrictedListPreference) { + f = RestrictedListPreference.RestrictedListPreferenceDialogFragment + .newInstance(preference.getKey()); + } else if (preference instanceof CustomListPreference) { f = CustomListPreference.CustomListPreferenceDialogFragment .newInstance(preference.getKey()); } else if (preference instanceof CustomDialogPreference) {