1 // Copyright 2020 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 package org.chromium.components.page_info; 5 6 import android.app.Dialog; 7 import android.os.Bundle; 8 import android.text.format.Formatter; 9 10 import androidx.appcompat.app.AlertDialog; 11 import androidx.preference.Preference; 12 13 import org.chromium.base.Callback; 14 import org.chromium.components.browser_ui.settings.ChromeImageViewPreference; 15 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference; 16 import org.chromium.components.browser_ui.settings.SettingsUtils; 17 import org.chromium.components.browser_ui.site_settings.SiteSettingsPreferenceFragment; 18 import org.chromium.components.content_settings.CookieControlsStatus; 19 import org.chromium.ui.text.NoUnderlineClickableSpan; 20 import org.chromium.ui.text.SpanApplier; 21 22 /** 23 * View showing a toggle and a description for third-party cookie blocking for a site. 24 */ 25 public class PageInfoCookiesPreference extends SiteSettingsPreferenceFragment { 26 private static final String COOKIE_SUMMARY_PREFERENCE = "cookie_summary"; 27 private static final String COOKIE_SWITCH_PREFERENCE = "cookie_switch"; 28 private static final String COOKIE_IN_USE_PREFERENCE = "cookie_in_use"; 29 private static final String CLEAR_BUTTON_PREFERENCE = "clear_button"; 30 31 private ChromeSwitchPreference mCookieSwitch; 32 private ChromeImageViewPreference mCookieInUse; 33 private Runnable mOnClearCallback; 34 private Dialog mConfirmationDialog; 35 36 /** Parameters to configure the cookie controls view. */ 37 public static class PageInfoCookiesViewParams { 38 // Called when the toggle controlling third-party cookie blocking changes. 39 public boolean thirdPartyCookieBlockingEnabled; 40 public Callback<Boolean> onCheckedChangedCallback; 41 public Runnable onClearCallback; 42 public Runnable onCookieSettingsLinkClicked; 43 public boolean disableCookieDeletion; 44 } 45 46 @Override onCreatePreferences(Bundle bundle, String s)47 public void onCreatePreferences(Bundle bundle, String s) { 48 // Remove this Preference if it is restored without SiteSettingsClient. 49 if (getSiteSettingsClient() == null) { 50 getParentFragmentManager().beginTransaction().remove(this).commit(); 51 return; 52 } 53 SettingsUtils.addPreferencesFromResource(this, R.xml.page_info_cookie_preference); 54 mCookieSwitch = findPreference(COOKIE_SWITCH_PREFERENCE); 55 mCookieInUse = findPreference(COOKIE_IN_USE_PREFERENCE); 56 } 57 58 @Override onDestroyView()59 public void onDestroyView() { 60 super.onDestroyView(); 61 if (mConfirmationDialog != null) { 62 mConfirmationDialog.dismiss(); 63 } 64 } 65 setParams(PageInfoCookiesViewParams params)66 public void setParams(PageInfoCookiesViewParams params) { 67 Preference cookieSummary = findPreference(COOKIE_SUMMARY_PREFERENCE); 68 NoUnderlineClickableSpan linkSpan = new NoUnderlineClickableSpan( 69 getResources(), (view) -> { params.onCookieSettingsLinkClicked.run(); }); 70 cookieSummary.setSummary( 71 SpanApplier.applySpans(getString(R.string.page_info_cookies_description), 72 new SpanApplier.SpanInfo("<link>", "</link>", linkSpan))); 73 74 // TODO(crbug.com/1077766): Set a ManagedPreferenceDelegate? 75 mCookieSwitch.setVisible(params.thirdPartyCookieBlockingEnabled); 76 mCookieSwitch.setOnPreferenceChangeListener((preference, newValue) -> { 77 params.onCheckedChangedCallback.onResult((Boolean) newValue); 78 return true; 79 }); 80 81 mCookieInUse.setIcon( 82 SettingsUtils.getTintedIcon(getContext(), R.drawable.permission_cookie)); 83 if (!params.disableCookieDeletion) { 84 mCookieInUse.setImageView( 85 R.drawable.ic_delete_white_24dp, R.string.page_info_cookies_clear, null); 86 mCookieInUse.setImageColor(R.color.default_icon_color_blue); 87 // Disabling enables passthrough of clicks to the main preference. 88 mCookieInUse.setImageViewEnabled(false); 89 mCookieInUse.setOnPreferenceClickListener(preference -> { 90 showClearCookiesConfirmation(); 91 return true; 92 }); 93 } 94 95 mOnClearCallback = params.onClearCallback; 96 } 97 showClearCookiesConfirmation()98 private void showClearCookiesConfirmation() { 99 mConfirmationDialog = 100 new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog) 101 .setTitle(R.string.page_info_cookies_clear) 102 .setMessage(R.string.page_info_cookies_clear_confirmation) 103 .setPositiveButton(R.string.page_info_cookies_clear_confirmation_button, 104 (dialog, which) -> mOnClearCallback.run()) 105 .setNegativeButton( 106 R.string.cancel, (dialog, which) -> mConfirmationDialog = null) 107 .show(); 108 } 109 setCookieBlockingStatus(@ookieControlsStatus int status, boolean isEnforced)110 public void setCookieBlockingStatus(@CookieControlsStatus int status, boolean isEnforced) { 111 boolean visible = status != CookieControlsStatus.DISABLED; 112 boolean enabled = status == CookieControlsStatus.ENABLED; 113 mCookieSwitch.setVisible(visible); 114 if (visible) { 115 mCookieSwitch.setIcon( 116 SettingsUtils.getTintedIcon(getContext(), R.drawable.ic_eye_crossed)); 117 mCookieSwitch.setChecked(enabled); 118 mCookieSwitch.setEnabled(!isEnforced); 119 } 120 } 121 setCookiesCount(int allowedCookies, int blockedCookies)122 public void setCookiesCount(int allowedCookies, int blockedCookies) { 123 mCookieSwitch.setSummary( 124 blockedCookies > 0 ? getContext().getResources().getQuantityString( 125 R.plurals.cookie_controls_blocked_cookies, blockedCookies, blockedCookies) 126 : null); 127 mCookieInUse.setTitle(getContext().getResources().getQuantityString( 128 R.plurals.page_info_cookies_in_use, allowedCookies, allowedCookies)); 129 } 130 setStorageUsage(long storageUsage)131 public void setStorageUsage(long storageUsage) { 132 mCookieInUse.setSummary( 133 storageUsage > 0 ? String.format( 134 getContext().getString(R.string.origin_settings_storage_usage_brief), 135 Formatter.formatShortFileSize(getContext(), storageUsage)) 136 : null); 137 } 138 } 139