Index: chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferences.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferences.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..53be86bd2daee987c9013fd01cf08fea89c506b9 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferences.java |
@@ -0,0 +1,757 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.preferences.website; |
+ |
+import android.os.Bundle; |
+import android.preference.Preference; |
+import android.preference.Preference.OnPreferenceChangeListener; |
+import android.preference.Preference.OnPreferenceClickListener; |
+import android.preference.PreferenceFragment; |
+import android.preference.PreferenceGroup; |
+import android.preference.PreferenceScreen; |
+import android.support.v4.view.MenuItemCompat; |
+import android.support.v7.widget.SearchView; |
+import android.text.SpannableString; |
+import android.text.style.ForegroundColorSpan; |
+import android.view.Menu; |
+import android.view.MenuInflater; |
+import android.view.MenuItem; |
+import android.view.inputmethod.EditorInfo; |
+import android.widget.ListView; |
+import android.widget.TextView; |
+ |
+import org.chromium.chrome.R; |
+import org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference; |
+import org.chromium.chrome.browser.preferences.ChromeBasePreference; |
+import org.chromium.chrome.browser.preferences.ChromeSwitchPreference; |
+import org.chromium.chrome.browser.preferences.ExpandablePreferenceGroup; |
+import org.chromium.chrome.browser.preferences.LocationSettings; |
+import org.chromium.chrome.browser.preferences.ManagedPreferenceDelegate; |
+import org.chromium.chrome.browser.preferences.ManagedPreferencesUtils; |
+import org.chromium.chrome.browser.preferences.PrefServiceBridge; |
+import org.chromium.ui.text.SpanApplier; |
+import org.chromium.ui.text.SpanApplier.SpanInfo; |
+ |
+import java.util.ArrayList; |
+import java.util.Collections; |
+import java.util.HashMap; |
+import java.util.HashSet; |
+import java.util.LinkedList; |
+import java.util.List; |
+import java.util.Map; |
+import java.util.Set; |
+ |
+/** |
+ * Shows a list of sites with their associated HTML5 settings. When the |
+ * users selects a site, a SingleWebsitePreferences fragment is launched to |
+ * allow the user to modify the settings. |
+ */ |
+public class WebsitePreferences extends PreferenceFragment |
+ implements OnPreferenceChangeListener, OnPreferenceClickListener { |
+ // The key to use to pass which category this preference should display, |
+ // e.g. Location/Popups/All sites (if blank). |
+ public static final String EXTRA_CATEGORY = "category"; |
+ public static final String EXTRA_TITLE = "title"; |
+ |
+ // This is a 0..1 <--> 1..N mapping between origin and Website. |
+ private final Map<String, Set<Website>> mSitesByOrigin = |
+ new HashMap<String, Set<Website>>(); |
+ // This is a 1 <--> 1..N mapping between host and Website. |
+ private final Map<String, Set<Website>> mSitesByHost = |
+ new HashMap<String, Set<Website>>(); |
+ |
+ // The view to show when the list is empty. |
+ private TextView mEmptyView; |
+ // The view for searching the list of items. |
+ private SearchView mSearchView; |
+ // What category the list is filtered by (e.g. show all, location, storage, |
+ // etc). For full list see WebsiteSettingsCategoryFilter. |
+ private String mCategoryFilter = ""; |
+ // The filter helper object. |
+ private WebsiteSettingsCategoryFilter mFilter = null; |
+ // If not blank, represents a substring to use to search for site names. |
+ private String mSearch = ""; |
+ // Whether to group by allowed/blocked list. |
+ private boolean mGroupByAllowBlock = false; |
+ // Whether the Blocked list should be shown expanded. |
+ private boolean mBlockListExpanded = false; |
+ // Whether the Allowed list should be shown expanded. |
+ private boolean mAllowListExpanded = true; |
+ // Whether this is the first time this screen is shown. |
+ private boolean mIsInitialRun = true; |
+ // The number of sites that are on the Allowed list. |
+ private int mAllowedSiteCount = 0; |
+ |
+ // Keys for individual preferences. |
+ public static final String READ_WRITE_TOGGLE_KEY = "read_write_toggle"; |
+ public static final String THIRD_PARTY_COOKIES_TOGGLE_KEY = "third_party_cookies"; |
+ // Keys for Allowed/Blocked preference groups/headers. |
+ private static final String ALLOWED_GROUP = "allowed_group"; |
+ private static final String BLOCKED_GROUP = "blocked_group"; |
+ |
+ // It is required that features fetching is serialized, as we need to have |
+ // all origins in place prior to populating hosts. |
+ private interface Task { |
+ void run(TaskQueue queue); |
+ } |
+ |
+ private static class TaskQueue extends LinkedList<Task> { |
+ void next() { |
+ if (!isEmpty()) removeFirst().run(this); |
+ } |
+ } |
+ |
+ private void getInfoForOrigins() { |
+ mSitesByOrigin.clear(); |
+ mSitesByHost.clear(); |
+ // Populate features from more specific to less specific. |
+ // Geolocation lookup permission is per-origin and per-embedder. |
+ TaskQueue queue = new TaskQueue(); |
+ if (mFilter.showAllSites(mCategoryFilter)) { |
+ queue.add(new GeolocationInfoFetcher()); |
+ // Midi sysex access permission is per-origin and per-embedder. |
+ queue.add(new MidiInfoFetcher()); |
+ // Cookies are stored per-origin. |
+ queue.add(new CookieInfoFetcher()); |
+ // Local storage info is per-origin. |
+ queue.add(new LocalStorageInfoFetcher()); |
+ // Website storage is per-host. |
+ queue.add(new WebStorageInfoFetcher()); |
+ // Popup exceptions are host-based patterns (unless we start |
+ // synchronizing popup exceptions with desktop Chrome.) |
+ queue.add(new PopupExceptionInfoFetcher()); |
+ // Protected media identifier permission is per-origin and per-embedder. |
+ queue.add(new ProtectedMediaIdentifierInfoFetcher()); |
+ if (ContentPreferences.pushNotificationsSupported()) { |
+ // Push notification permission is per-origin and per-embedder. |
+ queue.add(new PushNotificationInfoFetcher()); |
+ } |
+ // Voice and Video capture permission is per-origin and per-embedder. |
+ queue.add(new VoiceAndVideoCaptureInfoFetcher()); |
+ } else if (mFilter.showGeolocationSites(mCategoryFilter)) { |
+ queue.add(new GeolocationInfoFetcher()); |
+ } else if (mFilter.showCookiesSites(mCategoryFilter)) { |
+ queue.add(new CookieInfoFetcher()); |
+ } else if (mFilter.showStorageSites(mCategoryFilter)) { |
+ queue.add(new LocalStorageInfoFetcher()); |
+ } else if (mFilter.showCameraMicSites(mCategoryFilter)) { |
+ queue.add(new VoiceAndVideoCaptureInfoFetcher()); |
+ } else if (mFilter.showPopupSites(mCategoryFilter)) { |
+ queue.add(new PopupExceptionInfoFetcher()); |
+ } else if (mFilter.showPushNotificationsSites(mCategoryFilter)) { |
+ queue.add(new PushNotificationInfoFetcher()); |
+ } |
+ queue.add(new ResultsPopulator()); |
+ queue.next(); |
+ } |
+ |
+ private class GeolocationInfoFetcher implements Task { |
+ @Override |
+ public void run(TaskQueue queue) { |
+ for (GeolocationInfo info : WebsitePreferenceBridge.getGeolocationInfo()) { |
+ WebsiteAddress address = WebsiteAddress.create(info.getOrigin()); |
+ if (address == null) continue; |
+ createSiteByOrigin(address).setGeolocationInfo(info); |
+ } |
+ queue.next(); |
+ } |
+ } |
+ |
+ private class MidiInfoFetcher implements Task { |
+ @Override |
+ public void run(TaskQueue queue) { |
+ for (MidiInfo info : WebsitePreferenceBridge.getMidiInfo()) { |
+ WebsiteAddress address = WebsiteAddress.create(info.getOrigin()); |
+ if (address == null) continue; |
+ createSiteByOrigin(address).setMidiInfo(info); |
+ } |
+ queue.next(); |
+ } |
+ } |
+ |
+ private class PopupExceptionInfoFetcher implements Task { |
+ @Override |
+ public void run(TaskQueue queue) { |
+ for (PopupExceptionInfo info : WebsitePreferenceBridge.getPopupExceptionInfo()) { |
+ // The pattern "*" represents the default setting, not a specific website. |
+ if (info.getPattern().equals("*")) continue; |
+ WebsiteAddress address = WebsiteAddress.create(info.getPattern()); |
+ if (address == null) continue; |
+ Set<Website> sites = findOrCreateSitesByHost(address); |
+ for (Website site : sites) { |
+ site.setPopupExceptionInfo(info); |
+ } |
+ } |
+ queue.next(); |
+ } |
+ } |
+ |
+ private class CookieInfoFetcher implements Task { |
+ @Override |
+ public void run(TaskQueue queue) { |
+ for (CookieInfo info : WebsitePreferenceBridge.getCookieInfo()) { |
+ WebsiteAddress address = WebsiteAddress.create(info.getOrigin()); |
+ if (address == null) continue; |
+ createSiteByOrigin(address).setCookieInfo(info); |
+ } |
+ queue.next(); |
+ } |
+ } |
+ |
+ private class LocalStorageInfoFetcher implements Task { |
+ @Override |
+ public void run(final TaskQueue queue) { |
+ WebsitePreferenceBridge.fetchLocalStorageInfo( |
+ new WebsitePreferenceBridge.LocalStorageInfoReadyCallback() { |
+ @SuppressWarnings("unchecked") |
+ @Override |
+ public void onLocalStorageInfoReady(HashMap map) { |
+ for (Object o : map.entrySet()) { |
+ Map.Entry<String, LocalStorageInfo> entry = |
+ (Map.Entry<String, LocalStorageInfo>) o; |
+ WebsiteAddress address = WebsiteAddress.create(entry.getKey()); |
+ if (address == null) continue; |
+ Set<Website> sites = findOrCreateSitesByOrigin(address); |
+ for (Website site : sites) { |
+ site.setLocalStorageInfo(entry.getValue()); |
+ } |
+ } |
+ queue.next(); |
+ } |
+ }); |
+ } |
+ } |
+ |
+ private class WebStorageInfoFetcher implements Task { |
+ @Override |
+ public void run(final TaskQueue queue) { |
+ WebsitePreferenceBridge.fetchStorageInfo( |
+ new WebsitePreferenceBridge.StorageInfoReadyCallback() { |
+ @SuppressWarnings("unchecked") |
+ @Override |
+ public void onStorageInfoReady(ArrayList array) { |
+ ArrayList<StorageInfo> infoArray = array; |
+ for (StorageInfo info : infoArray) { |
+ WebsiteAddress address = WebsiteAddress.create(info.getHost()); |
+ if (address == null) continue; |
+ Set<Website> sites = findOrCreateSitesByHost(address); |
+ for (Website site : sites) { |
+ site.addStorageInfo(info); |
+ } |
+ } |
+ queue.next(); |
+ } |
+ }); |
+ } |
+ } |
+ |
+ private class ProtectedMediaIdentifierInfoFetcher implements Task { |
+ @Override |
+ public void run(TaskQueue queue) { |
+ for (ProtectedMediaIdentifierInfo info : |
+ WebsitePreferenceBridge.getProtectedMediaIdentifierInfo()) { |
+ WebsiteAddress address = WebsiteAddress.create(info.getOrigin()); |
+ if (address == null) continue; |
+ createSiteByOrigin(address).setProtectedMediaIdentifierInfo(info); |
+ } |
+ queue.next(); |
+ } |
+ } |
+ |
+ private class PushNotificationInfoFetcher implements Task { |
+ @Override |
+ public void run(TaskQueue queue) { |
+ for (PushNotificationInfo info : WebsitePreferenceBridge.getPushNotificationInfo()) { |
+ WebsiteAddress address = WebsiteAddress.create(info.getOrigin()); |
+ if (address == null) continue; |
+ createSiteByOrigin(address).setPushNotificationInfo(info); |
+ } |
+ queue.next(); |
+ } |
+ } |
+ |
+ private class VoiceAndVideoCaptureInfoFetcher implements Task { |
+ @Override |
+ public void run(TaskQueue queue) { |
+ for (VoiceAndVideoCaptureInfo info : |
+ WebsitePreferenceBridge.getVoiceAndVideoCaptureInfo()) { |
+ WebsiteAddress address = WebsiteAddress.create(info.getOrigin()); |
+ if (address == null) continue; |
+ createSiteByOrigin(address).setVoiceAndVideoCaptureInfo(info); |
+ } |
+ queue.next(); |
+ } |
+ } |
+ |
+ private void displayEmptyScreenMessage() { |
+ if (mEmptyView != null) { |
+ mEmptyView.setText(R.string.no_saved_website_settings); |
+ } |
+ } |
+ |
+ private class ResultsPopulator implements Task { |
+ @Override |
+ public void run(TaskQueue queue) { |
+ // Although a preferences fragment should normally be bound to an |
+ // activity, crash reports suggest that this isn't always true. |
+ // Preferences can't be instantiated without a context, so we are |
+ // bailing out. |
+ if (getActivity() == null) { |
+ queue.next(); |
+ return; |
+ } |
+ // First we scan origins to get settings from there. |
+ List<WebsitePreference> websites = new ArrayList<WebsitePreference>(); |
+ Set<Website> displayedSites = new HashSet<Website>(); |
+ for (Map.Entry<String, Set<Website>> element : mSitesByOrigin.entrySet()) { |
+ for (Website site : element.getValue()) { |
+ if (mSearch.isEmpty() || site.getTitle().contains(mSearch)) { |
+ websites.add(new WebsitePreference(getActivity(), site, mCategoryFilter)); |
+ displayedSites.add(site); |
+ } |
+ } |
+ } |
+ // Next we add sites that are only accessible by host name. |
+ for (Map.Entry<String, Set<Website>> element : mSitesByHost.entrySet()) { |
+ for (Website site : element.getValue()) { |
+ if (!displayedSites.contains(site)) { |
+ if (mSearch.isEmpty() || site.getTitle().contains(mSearch)) { |
+ websites.add(new WebsitePreference( |
+ getActivity(), site, mCategoryFilter)); |
+ displayedSites.add(site); |
+ } |
+ } |
+ } |
+ } |
+ |
+ resetList(); |
+ Collections.sort(websites); |
+ mAllowedSiteCount = 0; |
+ int blocked = 0; |
+ if (websites.size() > 0) { |
+ if (!mGroupByAllowBlock) { |
+ // We're not grouping sites into Allowed/Blocked lists, so show all in order |
+ // (will be alphabetical). |
+ for (WebsitePreference website : websites) { |
+ getPreferenceScreen().addPreference(website); |
+ } |
+ } else { |
+ // Group sites into Allowed/Blocked lists. |
+ PreferenceGroup allowedGroup = |
+ (PreferenceGroup) getPreferenceScreen().findPreference( |
+ ALLOWED_GROUP); |
+ PreferenceGroup blockedGroup = |
+ (PreferenceGroup) getPreferenceScreen().findPreference( |
+ BLOCKED_GROUP); |
+ |
+ for (WebsitePreference website : websites) { |
+ if (isOnBlockList(website)) { |
+ blockedGroup.addPreference(website); |
+ blocked += 1; |
+ } else { |
+ allowedGroup.addPreference(website); |
+ mAllowedSiteCount += 1; |
+ } |
+ } |
+ |
+ // The default, when the two lists are shown for the first time, is for the |
+ // Blocked list to be collapsed and Allowed expanded -- because the data in |
+ // the Allowed list is normally more useful than the data in the Blocked |
+ // list. A collapsed initial Blocked list works well *except* when there's |
+ // nothing in the Allowed list because then there's only Blocked items to |
+ // show and it doesn't make sense for those items to be hidden. So, in that |
+ // case (and only when the list is shown for the first time) do we ignore |
+ // the collapsed directive. The user can still collapse and expand the |
+ // Blocked list at will. |
+ if (mIsInitialRun) { |
+ if (allowedGroup.getPreferenceCount() == 0) mBlockListExpanded = true; |
+ mIsInitialRun = false; |
+ } |
+ |
+ if (!mBlockListExpanded) { |
+ blockedGroup.removeAll(); |
+ } |
+ |
+ if (!mAllowListExpanded) { |
+ allowedGroup.removeAll(); |
+ } |
+ } |
+ |
+ updateBlockedHeader(blocked); |
+ ChromeSwitchPreference globalToggle = (ChromeSwitchPreference) |
+ getPreferenceScreen().findPreference(READ_WRITE_TOGGLE_KEY); |
+ updateAllowedHeader(mAllowedSiteCount, |
+ (globalToggle != null ? globalToggle.isChecked() : true)); |
+ } else { |
+ displayEmptyScreenMessage(); |
+ updateBlockedHeader(0); |
+ updateAllowedHeader(0, true); |
+ } |
+ |
+ queue.next(); |
+ } |
+ } |
+ |
+ /** |
+ * Returns whether a website is on the Blocked list for the category currently showing (if any). |
+ * @param website The website to check. |
+ */ |
+ private boolean isOnBlockList(WebsitePreference website) { |
+ if (mFilter.showCookiesSites(mCategoryFilter)) { |
+ return website.site().getCookiePermission() == ContentSetting.BLOCK; |
+ } else if (mFilter.showCameraMicSites(mCategoryFilter)) { |
+ return website.site().getVoiceCapturePermission() == ContentSetting.BLOCK |
+ || website.site().getVideoCapturePermission() == ContentSetting.BLOCK; |
+ } else if (mFilter.showGeolocationSites(mCategoryFilter)) { |
+ return website.site().getGeolocationPermission() == ContentSetting.BLOCK; |
+ } else if (mFilter.showPopupSites(mCategoryFilter)) { |
+ return website.site().getPopupPermission() == ContentSetting.BLOCK; |
+ } else if (mFilter.showPushNotificationsSites(mCategoryFilter)) { |
+ return website.site().getPushNotificationPermission() == ContentSetting.BLOCK; |
+ } |
+ |
+ return false; |
+ } |
+ |
+ /** |
+ * Update the Category Header for the Allowed list. |
+ * @param numAllowed The number of sites that are on the Allowed list |
+ * @param toggleValue The value the global toggle will have once precessing ends. |
+ */ |
+ private void updateAllowedHeader(int numAllowed, boolean toggleValue) { |
+ ExpandablePreferenceGroup allowedGroup = |
+ (ExpandablePreferenceGroup) getPreferenceScreen().findPreference(ALLOWED_GROUP); |
+ if (numAllowed == 0) { |
+ if (allowedGroup != null) getPreferenceScreen().removePreference(allowedGroup); |
+ return; |
+ } |
+ if (!mGroupByAllowBlock) return; |
+ |
+ // When the toggle is set to Blocked, the Allowed list header should read 'Exceptions', not |
+ // 'Allowed' (because it shows exceptions from the rule). |
+ int resourceId = toggleValue |
+ ? R.string.website_settings_allowed_group_heading |
+ : R.string.website_settings_exceptions_group_heading; |
+ |
+ // Set the title and arrow icons for the header. |
+ allowedGroup.setGroupTitle(resourceId, numAllowed); |
+ allowedGroup.setIcon( |
+ mAllowListExpanded ? R.drawable.ic_expand_less : R.drawable.ic_expand_more); |
+ } |
+ |
+ private void updateBlockedHeader(int numBlocked) { |
+ ExpandablePreferenceGroup blockedGroup = |
+ (ExpandablePreferenceGroup) getPreferenceScreen().findPreference(BLOCKED_GROUP); |
+ if (numBlocked == 0) { |
+ if (blockedGroup != null) getPreferenceScreen().removePreference(blockedGroup); |
+ return; |
+ } |
+ if (!mGroupByAllowBlock) return; |
+ |
+ // Set the title and arrow icons for the header. |
+ blockedGroup.setGroupTitle(R.string.website_settings_blocked_group_heading, numBlocked); |
+ blockedGroup.setIcon( |
+ mBlockListExpanded ? R.drawable.ic_expand_less : R.drawable.ic_expand_more); |
+ } |
+ |
+ private Website createSiteByOrigin(WebsiteAddress address) { |
+ String origin = address.getOrigin(); |
+ String host = address.getHost(); |
+ Website site = new Website(address); |
+ if (!mSitesByOrigin.containsKey(origin)) |
+ mSitesByOrigin.put(origin, new HashSet<Website>()); |
+ mSitesByOrigin.get(origin).add(site); |
+ if (!mSitesByHost.containsKey(host)) |
+ mSitesByHost.put(host, new HashSet<Website>()); |
+ mSitesByHost.get(host).add(site); |
+ return site; |
+ } |
+ |
+ private Set<Website> findOrCreateSitesByOrigin(WebsiteAddress address) { |
+ String origin = address.getOrigin(); |
+ if (!mSitesByOrigin.containsKey(origin)) |
+ createSiteByOrigin(address); |
+ return mSitesByOrigin.get(origin); |
+ } |
+ |
+ private Set<Website> findOrCreateSitesByHost(WebsiteAddress address) { |
+ String host = address.getHost(); |
+ if (!mSitesByHost.containsKey(host)) { |
+ mSitesByHost.put(host, new HashSet<Website>()); |
+ mSitesByHost.get(host).add(new Website(address)); |
+ } |
+ return mSitesByHost.get(host); |
+ } |
+ |
+ @Override |
+ public void onActivityCreated(Bundle savedInstanceState) { |
+ addPreferencesFromResource(R.xml.website_settings_preferences); |
+ ListView listView = (ListView) getView().findViewById(android.R.id.list); |
+ mEmptyView = (TextView) getView().findViewById(android.R.id.empty); |
+ listView.setEmptyView(mEmptyView); |
+ |
+ // Read which category, if any, we should be showing. |
+ if (getArguments() != null) { |
+ mCategoryFilter = getArguments().getString(EXTRA_CATEGORY, ""); |
+ String title = getArguments().getString(EXTRA_TITLE); |
+ if (title != null) getActivity().setTitle(title); |
+ } |
+ |
+ mFilter = new WebsiteSettingsCategoryFilter(); |
+ |
+ configureGlobalToggles(); |
+ |
+ setHasOptionsMenu(true); |
+ |
+ super.onActivityCreated(savedInstanceState); |
+ } |
+ |
+ @Override |
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { |
+ inflater.inflate(R.menu.website_preferences_menu, menu); |
+ |
+ MenuItem searchItem = menu.findItem(R.id.search); |
+ mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem); |
+ mSearchView.setImeOptions(EditorInfo.IME_FLAG_NO_FULLSCREEN); |
+ |
+ SearchView.OnQueryTextListener queryTextListener = |
+ new SearchView.OnQueryTextListener() { |
+ @Override |
+ public boolean onQueryTextSubmit(String query) { |
+ return true; |
+ } |
+ |
+ @Override |
+ public boolean onQueryTextChange(String query) { |
+ if (query.equals(mSearch)) return true; |
+ |
+ mSearch = query; |
+ getInfoForOrigins(); |
+ return true; |
+ } |
+ }; |
+ mSearchView.setOnQueryTextListener(queryTextListener); |
+ } |
+ |
+ @Override |
+ public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { |
+ if (isCategoryManaged()) { |
+ showManagedToast(); |
+ return false; |
+ } |
+ |
+ if (!mSearch.isEmpty()) { |
+ // Clear out any lingering searches, so that the full list is shown |
+ // when coming back to this page. |
+ mSearch = ""; |
+ mSearchView.setQuery("", false); |
+ } |
+ |
+ if (preference instanceof WebsitePreference) { |
+ WebsitePreference website = (WebsitePreference) preference; |
+ website.setFragment(SingleWebsitePreferences.class.getName()); |
+ website.putSiteIntoExtras(SingleWebsitePreferences.EXTRA_SITE); |
+ } |
+ |
+ return super.onPreferenceTreeClick(screen, preference); |
+ } |
+ |
+ // OnPreferenceChangeListener: |
+ |
+ @Override |
+ public boolean onPreferenceChange(Preference preference, Object newValue) { |
+ if (READ_WRITE_TOGGLE_KEY.equals(preference.getKey())) { |
+ if (isCategoryManaged()) return false; |
+ |
+ if (mFilter.showGeolocationSites(mCategoryFilter)) { |
+ PrefServiceBridge.getInstance().setAllowLocationEnabled((boolean) newValue); |
+ } else if (mFilter.showCookiesSites(mCategoryFilter)) { |
+ PrefServiceBridge.getInstance().setAllowCookiesEnabled((boolean) newValue); |
+ updateThirdPartyCookiesCheckBox(); |
+ } else if (mFilter.showCameraMicSites(mCategoryFilter)) { |
+ PrefServiceBridge.getInstance().setCameraMicEnabled((boolean) newValue); |
+ } else if (mFilter.showPopupSites(mCategoryFilter)) { |
+ PrefServiceBridge.getInstance().setAllowPopupsEnabled((boolean) newValue); |
+ } else if (mFilter.showPushNotificationsSites(mCategoryFilter)) { |
+ PrefServiceBridge.getInstance().setPushNotificationsEnabled((boolean) newValue); |
+ } |
+ |
+ ChromeSwitchPreference globalToggle = (ChromeSwitchPreference) |
+ getPreferenceScreen().findPreference(READ_WRITE_TOGGLE_KEY); |
+ updateAllowedHeader(mAllowedSiteCount, !globalToggle.isChecked()); |
+ } else if (THIRD_PARTY_COOKIES_TOGGLE_KEY.equals(preference.getKey())) { |
+ PrefServiceBridge.getInstance().setBlockThirdPartyCookiesEnabled(!((boolean) newValue)); |
+ } |
+ return true; |
+ } |
+ |
+ // OnPreferenceClickListener: |
+ |
+ @Override |
+ public boolean onPreferenceClick(Preference preference) { |
+ if (ALLOWED_GROUP.equals(preference.getKey())) { |
+ mAllowListExpanded = !mAllowListExpanded; |
+ } else { |
+ mBlockListExpanded = !mBlockListExpanded; |
+ } |
+ getInfoForOrigins(); |
+ return true; |
+ } |
+ |
+ @Override |
+ public void onResume() { |
+ super.onResume(); |
+ |
+ getInfoForOrigins(); |
+ } |
+ |
+ /* |
+ * Returns whether the current category is managed either by enterprise policy or by the |
+ * custodian of a supervised account. |
+ */ |
+ private boolean isCategoryManaged() { |
+ PrefServiceBridge prefs = PrefServiceBridge.getInstance(); |
+ if (mFilter.showCookiesSites(mCategoryFilter)) return prefs.isAcceptCookiesManaged(); |
+ if (mFilter.showGeolocationSites(mCategoryFilter)) { |
+ return !prefs.isAllowLocationUserModifiable(); |
+ } |
+ if (mFilter.showCameraMicSites(mCategoryFilter)) return !prefs.isCameraMicUserModifiable(); |
+ if (mFilter.showPopupSites(mCategoryFilter)) return prefs.isPopupsManaged(); |
+ return false; |
+ } |
+ |
+ /* |
+ * Returns whether the current category is managed by the custodian (e.g. parent, not an |
+ * enterprise admin) of the account if the account is supervised. |
+ */ |
+ private boolean isCategoryManagedByCustodian() { |
+ PrefServiceBridge prefs = PrefServiceBridge.getInstance(); |
+ if (mFilter.showGeolocationSites(mCategoryFilter)) { |
+ return prefs.isAllowLocationManagedByCustodian(); |
+ } |
+ if (mFilter.showCameraMicSites(mCategoryFilter)) { |
+ return prefs.isCameraMicManagedByCustodian(); |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Reset the preference screen an initialize it again. |
+ */ |
+ private void resetList() { |
+ // This will remove the combo box at the top and all the sites listed below it. |
+ getPreferenceScreen().removeAll(); |
+ // And this will add the filter preference back (combo box). |
+ addPreferencesFromResource(R.xml.website_settings_preferences); |
+ |
+ configureGlobalToggles(); |
+ } |
+ |
+ private void configureGlobalToggles() { |
+ // Only some have a global toggle at the top. |
+ ChromeSwitchPreference globalToggle = (ChromeSwitchPreference) |
+ getPreferenceScreen().findPreference(READ_WRITE_TOGGLE_KEY); |
+ |
+ Preference thirdPartyCookies = getPreferenceScreen().findPreference( |
+ THIRD_PARTY_COOKIES_TOGGLE_KEY); |
+ |
+ if (mFilter.showCookiesSites(mCategoryFilter)) { |
+ thirdPartyCookies.setOnPreferenceChangeListener(this); |
+ updateThirdPartyCookiesCheckBox(); |
+ } else { |
+ getPreferenceScreen().removePreference(thirdPartyCookies); |
+ } |
+ |
+ if (mFilter.showAllSites(mCategoryFilter) |
+ || mFilter.showStorageSites(mCategoryFilter)) { |
+ getPreferenceScreen().removePreference(globalToggle); |
+ getPreferenceScreen().removePreference( |
+ getPreferenceScreen().findPreference(ALLOWED_GROUP)); |
+ getPreferenceScreen().removePreference( |
+ getPreferenceScreen().findPreference(BLOCKED_GROUP)); |
+ } else { |
+ // When this menu opens, make sure the Blocked list is collapsed. |
+ if (!mGroupByAllowBlock) { |
+ mBlockListExpanded = false; |
+ mAllowListExpanded = true; |
+ } |
+ mGroupByAllowBlock = true; |
+ PreferenceGroup allowedGroup = |
+ (PreferenceGroup) getPreferenceScreen().findPreference( |
+ ALLOWED_GROUP); |
+ PreferenceGroup blockedGroup = |
+ (PreferenceGroup) getPreferenceScreen().findPreference( |
+ BLOCKED_GROUP); |
+ allowedGroup.setOnPreferenceClickListener(this); |
+ blockedGroup.setOnPreferenceClickListener(this); |
+ |
+ // Determine what toggle to use and what it should display. |
+ int type = mFilter.toContentSettingsType(mCategoryFilter); |
+ Website.PermissionDataEntry entry = |
+ Website.PermissionDataEntry.getPermissionDataEntry(type); |
+ if (mFilter.showGeolocationSites(mCategoryFilter) |
+ && !isCategoryManaged() |
+ && !LocationSettings.getInstance().isSystemLocationSettingEnabled()) { |
+ getPreferenceScreen().removePreference(globalToggle); |
+ |
+ // Show the link to system settings since system location is disabled. |
+ ChromeBasePreference locationMessage = |
+ new ChromeBasePreference(getActivity(), null); |
+ int color = getResources().getColor(R.color.pref_accent_color); |
+ ForegroundColorSpan linkSpan = new ForegroundColorSpan(color); |
+ final String message = getString(R.string.android_location_off); |
+ final SpannableString messageWithLink = |
+ SpanApplier.applySpans( |
+ message, new SpanInfo("<link>", "</link>", linkSpan)); |
+ locationMessage.setTitle(messageWithLink); |
+ locationMessage.setIntent( |
+ LocationSettings.getInstance().getSystemLocationSettingsIntent()); |
+ getPreferenceScreen().addPreference(locationMessage); |
+ } else { |
+ globalToggle.setOnPreferenceChangeListener(this); |
+ globalToggle.setTitle(entry.titleResourceId); |
+ globalToggle.setSummaryOn(entry.getEnabledSummaryResourceId()); |
+ globalToggle.setSummaryOff(entry.getDisabledSummaryResourceId()); |
+ if (isCategoryManaged() && !isCategoryManagedByCustodian()) { |
+ globalToggle.setIcon(R.drawable.controlled_setting_mandatory); |
+ } |
+ if (mFilter.showGeolocationSites(mCategoryFilter)) { |
+ globalToggle.setChecked( |
+ LocationSettings.getInstance().isChromeLocationSettingEnabled()); |
+ } else if (mFilter.showCameraMicSites(mCategoryFilter)) { |
+ globalToggle.setChecked(PrefServiceBridge.getInstance().isCameraMicEnabled()); |
+ } else if (mFilter.showPopupSites(mCategoryFilter)) { |
+ globalToggle.setChecked(PrefServiceBridge.getInstance().popupsEnabled()); |
+ } else if (mFilter.showPushNotificationsSites(mCategoryFilter)) { |
+ globalToggle.setChecked( |
+ PrefServiceBridge.getInstance().isPushNotificationsEnabled()); |
+ } else if (mFilter.showCookiesSites(mCategoryFilter)) { |
+ globalToggle.setChecked( |
+ PrefServiceBridge.getInstance().isAcceptCookiesEnabled()); |
+ } |
+ } |
+ } |
+ } |
+ |
+ private void updateThirdPartyCookiesCheckBox() { |
+ ChromeBaseCheckBoxPreference thirdPartyCookiesPref = (ChromeBaseCheckBoxPreference) |
+ getPreferenceScreen().findPreference(THIRD_PARTY_COOKIES_TOGGLE_KEY); |
+ thirdPartyCookiesPref.setEnabled(PrefServiceBridge.getInstance().isAcceptCookiesEnabled()); |
+ thirdPartyCookiesPref.setManagedPreferenceDelegate(new ManagedPreferenceDelegate() { |
+ @Override |
+ public boolean isPreferenceControlledByPolicy(Preference preference) { |
+ return PrefServiceBridge.getInstance().isBlockThirdPartyCookiesManaged(); |
+ } |
+ }); |
+ } |
+ |
+ private void showManagedToast() { |
+ if (isCategoryManagedByCustodian()) { |
+ ManagedPreferencesUtils.showManagedByParentToast(getActivity()); |
+ } else { |
+ ManagedPreferencesUtils.showManagedByAdministratorToast(getActivity()); |
+ } |
+ } |
+} |