From e407e67a3b42d42beb44de50cb9ccdf2f559854f Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Fri, 5 May 2017 16:07:48 -0700 Subject: [PATCH] Create RestrictedDashboardFragment. RestrictedDashboardFragment has all the same logic coming from RestrcitedSettingsFragment but extends from DashboardFragment. As a result, we could use preferenceController in child class of RestrictedDashboardFragment. This cl also make DeviceListPreferenceFragment as child of RestrictedDashboardFragment, which enfluences the bluetooth page. Bug: 38041586 Test: Build Change-Id: I01395d506176c5cc584948478f7ca16c1c7c7045 --- .../settings/RestrictedSettingsFragment.java | 6 +- .../settings/bluetooth/BluetoothSettings.java | 17 +- .../DeviceListPreferenceFragment.java | 4 +- .../bluetooth/DevicePickerFragment.java | 21 +- .../RestrictedDashboardFragment.java | 261 ++++++++++++++++++ ...randfather_not_implementing_index_provider | 1 + 6 files changed, 303 insertions(+), 7 deletions(-) create mode 100644 src/com/android/settings/dashboard/RestrictedDashboardFragment.java diff --git a/src/com/android/settings/RestrictedSettingsFragment.java b/src/com/android/settings/RestrictedSettingsFragment.java index ce8a4add96a..bbb317ba420 100644 --- a/src/com/android/settings/RestrictedSettingsFragment.java +++ b/src/com/android/settings/RestrictedSettingsFragment.java @@ -17,7 +17,6 @@ package com.android.settings; import android.app.Activity; -import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -27,10 +26,10 @@ import android.os.Bundle; import android.os.PersistableBundle; import android.os.UserHandle; import android.os.UserManager; -import android.view.Gravity; import android.view.View; import android.widget.TextView; +import com.android.settings.dashboard.RestrictedDashboardFragment; import com.android.settingslib.RestrictedLockUtils; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; @@ -46,7 +45,10 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; * If this settings screen should be pin protected whenever * {@link RestrictionsManager.hasRestrictionsProvider()} returns true, pass in * {@link RESTRICT_IF_OVERRIDABLE} to the constructor instead of a restrictions key. + * + * @deprecated Use {@link RestrictedDashboardFragment} instead */ +@Deprecated public abstract class RestrictedSettingsFragment extends SettingsPreferenceFragment { protected static final String RESTRICT_IF_OVERRIDABLE = "restrict_if_overridable"; diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java index 317a350d80a..5c40d418d70 100644 --- a/src/com/android/settings/bluetooth/BluetoothSettings.java +++ b/src/com/android/settings/bluetooth/BluetoothSettings.java @@ -45,6 +45,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.LinkifyUtils; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.core.PreferenceController; import com.android.settings.dashboard.SummaryLoader; import com.android.settings.location.ScanningSettings; import com.android.settings.search.BaseSearchIndexProvider; @@ -168,7 +169,6 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem @Override void addPreferencesForActivity() { - addPreferencesFromResource(R.xml.bluetooth_settings); final Context prefContext = getPrefContext(); mPairedDevicesCategory = new PreferenceCategory(prefContext); mPairedDevicesCategory.setKey(KEY_PAIRED_DEVICES); @@ -517,6 +517,21 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem return R.string.help_url_bluetooth; } + @Override + protected String getLogTag() { + return TAG; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.bluetooth_settings; + } + + @Override + protected List getPreferenceControllers(Context context) { + return null; + } + @VisibleForTesting static class SummaryProvider implements SummaryLoader.SummaryProvider, OnSummaryChangeListener { diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java index c1321639d62..40343167943 100644 --- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java +++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java @@ -24,7 +24,7 @@ import android.support.v7.preference.PreferenceCategory; import android.support.v7.preference.PreferenceGroup; import android.util.Log; -import com.android.settings.RestrictedSettingsFragment; +import com.android.settings.dashboard.RestrictedDashboardFragment; import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.BluetoothDeviceFilter; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -42,7 +42,7 @@ import java.util.WeakHashMap; * @see DevicePickerFragment */ public abstract class DeviceListPreferenceFragment extends - RestrictedSettingsFragment implements BluetoothCallback { + RestrictedDashboardFragment implements BluetoothCallback { private static final String TAG = "DeviceListPreferenceFragment"; diff --git a/src/com/android/settings/bluetooth/DevicePickerFragment.java b/src/com/android/settings/bluetooth/DevicePickerFragment.java index 39aca14139f..8bf820262b7 100644 --- a/src/com/android/settings/bluetooth/DevicePickerFragment.java +++ b/src/com/android/settings/bluetooth/DevicePickerFragment.java @@ -29,16 +29,20 @@ import android.view.MenuItem; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; +import com.android.settings.core.PreferenceController; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH; +import java.util.List; + /** * BluetoothSettings is the Settings screen for Bluetooth configuration and * connection management. */ public final class DevicePickerFragment extends DeviceListPreferenceFragment { private static final int MENU_ID_REFRESH = Menu.FIRST; + private static final String TAG = "DevicePickerFragment"; public DevicePickerFragment() { super(null /* Not tied to any user restrictions. */); @@ -51,8 +55,6 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment { @Override void addPreferencesForActivity() { - addPreferencesFromResource(R.xml.device_picker); - Intent intent = getActivity().getIntent(); mNeedAuth = intent.getBooleanExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false); setFilter(intent.getIntExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE, @@ -150,6 +152,21 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment { } } + @Override + protected String getLogTag() { + return TAG; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.device_picker; + } + + @Override + protected List getPreferenceControllers(Context context) { + return null; + } + private void sendDevicePickedIntent(BluetoothDevice device) { Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); diff --git a/src/com/android/settings/dashboard/RestrictedDashboardFragment.java b/src/com/android/settings/dashboard/RestrictedDashboardFragment.java new file mode 100644 index 00000000000..c2176d7206b --- /dev/null +++ b/src/com/android/settings/dashboard/RestrictedDashboardFragment.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2017 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.dashboard; + +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.RestrictionsManager; +import android.os.Bundle; +import android.os.PersistableBundle; +import android.os.UserHandle; +import android.os.UserManager; +import android.view.View; +import android.widget.TextView; + +import com.android.settings.R; +import com.android.settings.RestrictedSettingsFragment; +import com.android.settings.ShowAdminSupportDetailsDialog; +import com.android.settingslib.RestrictedLockUtils; + +/** + * Base class for settings screens that should be pin protected when in restricted mode or + * that will display an admin support message in case an admin has disabled the options. + * The constructor for this class will take the restriction key that this screen should be + * locked by. If {@link RestrictionsManager.hasRestrictionsProvider()} and + * {@link UserManager.hasUserRestriction()}, then the user will have to enter the restrictions + * pin before seeing the Settings screen. + * + * {@link RestrictionsManager.hasRestrictionsProvider()} returns true, pass in + * {@link RESTRICT_IF_OVERRIDABLE} to the constructor instead of a restrictions key. + * + * This fragment is a replacement of {@link RestrictedSettingsFragment} but extends + * from {@link DashboardFragment}, so we could also use + * {@link com.android.settings.core.PreferenceController} in this fragment. + */ +public abstract class RestrictedDashboardFragment extends DashboardFragment { + + protected static final String RESTRICT_IF_OVERRIDABLE = "restrict_if_overridable"; + + // No RestrictedSettingsFragment screens should use this number in startActivityForResult. + private static final int REQUEST_PIN_CHALLENGE = 12309; + + private static final String KEY_CHALLENGE_SUCCEEDED = "chsc"; + private static final String KEY_CHALLENGE_REQUESTED = "chrq"; + + // If the restriction PIN is entered correctly. + private boolean mChallengeSucceeded; + private boolean mChallengeRequested; + + private UserManager mUserManager; + private RestrictionsManager mRestrictionsManager; + + private final String mRestrictionKey; + private View mAdminSupportDetails; + private EnforcedAdmin mEnforcedAdmin; + private TextView mEmptyTextView; + + private boolean mOnlyAvailableForAdmins = false; + private boolean mIsAdminUser; + + // Receiver to clear pin status when the screen is turned off. + private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!mChallengeRequested) { + mChallengeSucceeded = false; + mChallengeRequested = false; + } + } + }; + + /** + * @param restrictionKey The restriction key to check before pin protecting + * this settings page. Pass in {@link RESTRICT_IF_OVERRIDABLE} if it should + * be protected whenever a restrictions provider is set. Pass in + * null if it should never be protected. + */ + public RestrictedDashboardFragment(String restrictionKey) { + mRestrictionKey = restrictionKey; + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mRestrictionsManager = (RestrictionsManager) getSystemService(Context.RESTRICTIONS_SERVICE); + mUserManager = (UserManager) getSystemService(Context.USER_SERVICE); + mIsAdminUser = mUserManager.isAdminUser(); + + if (icicle != null) { + mChallengeSucceeded = icicle.getBoolean(KEY_CHALLENGE_SUCCEEDED, false); + mChallengeRequested = icicle.getBoolean(KEY_CHALLENGE_REQUESTED, false); + } + + IntentFilter offFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF); + offFilter.addAction(Intent.ACTION_USER_PRESENT); + getActivity().registerReceiver(mScreenOffReceiver, offFilter); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mAdminSupportDetails = initAdminSupportDetailsView(); + mEmptyTextView = initEmptyTextView(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + if (getActivity().isChangingConfigurations()) { + outState.putBoolean(KEY_CHALLENGE_REQUESTED, mChallengeRequested); + outState.putBoolean(KEY_CHALLENGE_SUCCEEDED, mChallengeSucceeded); + } + } + + @Override + public void onResume() { + super.onResume(); + + if (shouldBeProviderProtected(mRestrictionKey)) { + ensurePin(); + } + } + + @Override + public void onDestroy() { + getActivity().unregisterReceiver(mScreenOffReceiver); + super.onDestroy(); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_PIN_CHALLENGE) { + if (resultCode == Activity.RESULT_OK) { + mChallengeSucceeded = true; + mChallengeRequested = false; + } else { + mChallengeSucceeded = false; + } + return; + } + + super.onActivityResult(requestCode, resultCode, data); + } + + private void ensurePin() { + if (!mChallengeSucceeded && !mChallengeRequested + && mRestrictionsManager.hasRestrictionsProvider()) { + Intent intent = mRestrictionsManager.createLocalApprovalIntent(); + if (intent != null) { + mChallengeRequested = true; + mChallengeSucceeded = false; + PersistableBundle request = new PersistableBundle(); + request.putString(RestrictionsManager.REQUEST_KEY_MESSAGE, + getResources().getString(R.string.restr_pin_enter_admin_pin)); + intent.putExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE, request); + startActivityForResult(intent, REQUEST_PIN_CHALLENGE); + } + } + } + + /** + * Returns true if this activity is restricted, but no restrictions provider has been set. + * Used to determine if the settings UI should disable UI. + */ + protected boolean isRestrictedAndNotProviderProtected() { + if (mRestrictionKey == null || RESTRICT_IF_OVERRIDABLE.equals(mRestrictionKey)) { + return false; + } + return mUserManager.hasUserRestriction(mRestrictionKey) + && !mRestrictionsManager.hasRestrictionsProvider(); + } + + protected boolean hasChallengeSucceeded() { + return (mChallengeRequested && mChallengeSucceeded) || !mChallengeRequested; + } + + /** + * Returns true if this restrictions key is locked down. + */ + protected boolean shouldBeProviderProtected(String restrictionKey) { + if (restrictionKey == null) { + return false; + } + boolean restricted = RESTRICT_IF_OVERRIDABLE.equals(restrictionKey) + || mUserManager.hasUserRestriction(mRestrictionKey); + return restricted && mRestrictionsManager.hasRestrictionsProvider(); + } + + private View initAdminSupportDetailsView() { + return getActivity().findViewById(R.id.admin_support_details); + } + + protected TextView initEmptyTextView() { + TextView emptyView = (TextView) getActivity().findViewById(android.R.id.empty); + return emptyView; + } + + public EnforcedAdmin getRestrictionEnforcedAdmin() { + mEnforcedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(getActivity(), + mRestrictionKey, UserHandle.myUserId()); + if (mEnforcedAdmin != null && mEnforcedAdmin.userId == UserHandle.USER_NULL) { + mEnforcedAdmin.userId = UserHandle.myUserId(); + } + return mEnforcedAdmin; + } + + public TextView getEmptyTextView() { + return mEmptyTextView; + } + + @Override + protected void onDataSetChanged() { + highlightPreferenceIfNeeded(); + if (mAdminSupportDetails != null && isUiRestrictedByOnlyAdmin()) { + final EnforcedAdmin admin = getRestrictionEnforcedAdmin(); + ShowAdminSupportDetailsDialog.setAdminSupportDetails(getActivity(), + mAdminSupportDetails, admin, false); + setEmptyView(mAdminSupportDetails); + } else if (mEmptyTextView != null) { + setEmptyView(mEmptyTextView); + } + super.onDataSetChanged(); + } + + public void setIfOnlyAvailableForAdmins(boolean onlyForAdmins) { + mOnlyAvailableForAdmins = onlyForAdmins; + } + + /** + * Returns whether restricted or actionable UI elements should be removed or disabled. + */ + protected boolean isUiRestricted() { + return isRestrictedAndNotProviderProtected() || !hasChallengeSucceeded() + || (!mIsAdminUser && mOnlyAvailableForAdmins); + } + + protected boolean isUiRestrictedByOnlyAdmin() { + return isUiRestricted() && !mUserManager.hasBaseUserRestriction(mRestrictionKey, + UserHandle.of(UserHandle.myUserId())) && (mIsAdminUser || !mOnlyAvailableForAdmins); + } +} diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider index b985226e83c..2421cbbb76d 100644 --- a/tests/robotests/assets/grandfather_not_implementing_index_provider +++ b/tests/robotests/assets/grandfather_not_implementing_index_provider @@ -1,3 +1,4 @@ +com.android.settings.bluetooth.DevicePickerFragment com.android.settings.language.LanguageAndRegionSettings com.android.settings.notification.ZenModePrioritySettings com.android.settings.accounts.AccountDetailDashboardFragment