| 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());
|
| + }
|
| + }
|
| +}
|
|
|