// Copyright 2022 The Chromium Authors
// 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.privacy_sandbox;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.base.supplier.ObservableSupplierImpl;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
import org.chromium.components.browser_ui.settings.ClickableSpansTextMessagePreference;
import org.chromium.components.browser_ui.settings.SettingsUtils;
import org.chromium.components.browser_ui.settings.TextMessagePreference;
import org.chromium.components.prefs.PrefService;
import org.chromium.components.user_prefs.UserPrefs;
import org.chromium.ui.text.NoUnderlineClickableSpan;
import org.chromium.ui.text.SpanApplier;
import java.util.List;
/** Fragment for the Privacy Sandbox -> Topic preferences. */
public class TopicsFragment extends PrivacySandboxSettingsBaseFragment
implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener {
private static final String TOPICS_TOGGLE_PREFERENCE = "topics_toggle";
private static final String TOPICS_EXPLANATION_PREFERENCE = "topics_explanation";
private static final String TOPICS_HEADING_PREFERENCE = "topics_heading";
private static final String CURRENT_TOPICS_PREFERENCE = "current_topics";
private static final String EMPTY_TOPICS_PREFERENCE = "topics_empty";
private static final String DISABLED_TOPICS_PREFERENCE = "topics_disabled";
private static final String TOPICS_PAGE_FOOTER_PREFERENCE = "topics_page_footer";
private static final String ACTIVE_TOPICS_PREFERENCE = "active_topics";
private static final String BLOCKED_TOPICS_PREFERENCE = "blocked_topics";
private static final String MANAGE_TOPICS_PREFERENCE = "manage_topics";
private ChromeSwitchPreference mTopicsTogglePreference;
private TextMessagePreference mTopicsExplanationPreference;
private PreferenceCategoryWithClickableSummary mTopicsHeadingPreference;
private PreferenceCategory mCurrentTopicsCategory;
private TextMessagePreference mEmptyTopicsPreference;
private TextMessagePreference mDisabledTopicsPreference;
private ClickableSpansTextMessagePreference mTopicsPageFooterPreference;
private Preference mActiveTopicsPreference;
private Preference mBlockedTopicsPreference;
private Preference mManageTopicsPreference;
private final ObservableSupplierImpl<String> mPageTitle = new ObservableSupplierImpl<>();
static boolean isTopicsPrefEnabled(Profile profile) {
PrefService prefService = UserPrefs.get(profile);
return prefService.getBoolean(Pref.PRIVACY_SANDBOX_M1_TOPICS_ENABLED);
}
static void setTopicsPrefEnabled(Profile profile, boolean isEnabled) {
PrefService prefService = UserPrefs.get(profile);
prefService.setBoolean(Pref.PRIVACY_SANDBOX_M1_TOPICS_ENABLED, isEnabled);
}
static boolean isTopicsPrefManaged(Profile profile) {
PrefService prefService = UserPrefs.get(profile);
return prefService.isManagedPreference(Pref.PRIVACY_SANDBOX_M1_TOPICS_ENABLED);
}
@Override
public void onCreatePreferences(@Nullable Bundle bundle, @Nullable String s) {
super.onCreatePreferences(bundle, s);
mPageTitle.set(getString(R.string.settings_topics_page_title));
if (ChromeFeatureList.isEnabled(
ChromeFeatureList.PRIVACY_SANDBOX_PROACTIVE_TOPICS_BLOCKING)) {
SettingsUtils.addPreferencesFromResource(this, R.xml.topics_preference_v2);
} else {
SettingsUtils.addPreferencesFromResource(this, R.xml.topics_preference);
}
mTopicsTogglePreference = findPreference(TOPICS_TOGGLE_PREFERENCE);
mTopicsExplanationPreference = findPreference(TOPICS_EXPLANATION_PREFERENCE);
mTopicsHeadingPreference = findPreference(TOPICS_HEADING_PREFERENCE);
mCurrentTopicsCategory = findPreference(CURRENT_TOPICS_PREFERENCE);
mEmptyTopicsPreference = findPreference(EMPTY_TOPICS_PREFERENCE);
mDisabledTopicsPreference = findPreference(DISABLED_TOPICS_PREFERENCE);
mTopicsPageFooterPreference =
(ClickableSpansTextMessagePreference) findPreference(TOPICS_PAGE_FOOTER_PREFERENCE);
mActiveTopicsPreference = findPreference(ACTIVE_TOPICS_PREFERENCE);
mBlockedTopicsPreference = findPreference(BLOCKED_TOPICS_PREFERENCE);
mManageTopicsPreference = findPreference(MANAGE_TOPICS_PREFERENCE);
mTopicsTogglePreference.setChecked(isTopicsPrefEnabled(getProfile()));
mTopicsTogglePreference.setOnPreferenceChangeListener(this);
mTopicsTogglePreference.setManagedPreferenceDelegate(createManagedPreferenceDelegate());
if (!ChromeFeatureList.isEnabled(
ChromeFeatureList.PRIVACY_SANDBOX_PROACTIVE_TOPICS_BLOCKING)) {
mTopicsHeadingPreference.setSummary(
SpanApplier.applySpans(getResources().getString(
R.string.settings_topics_page_current_topics_description),
new SpanApplier.SpanInfo(
"<link>",
"</link>",
new NoUnderlineClickableSpan(
getContext(), this::onLearnMoreClicked))));
mTopicsPageFooterPreference.setSummary(
SpanApplier.applySpans(
getResources().getString(R.string.settings_topics_page_footer),
new SpanApplier.SpanInfo(
"<link1>",
"</link1>",
new NoUnderlineClickableSpan(
getContext(), this::onFledgeSettingsLinkClicked)),
new SpanApplier.SpanInfo(
"<link2>",
"</link2>",
new NoUnderlineClickableSpan(
getContext(), this::onCookieSettingsLink))));
return;
}
mTopicsExplanationPreference.setSummary(
SpanApplier.applySpans(
getResources().getString(R.string.settings_topics_page_disclaimer_clank),
new SpanApplier.SpanInfo(
"<link1>",
"</link1>",
new NoUnderlineClickableSpan(
getContext(), this::onManagingAdPrivacyClicked))));
mTopicsPageFooterPreference.setSummary(
SpanApplier.applySpans(
getResources().getString(R.string.settings_topics_page_footer_new),
new SpanApplier.SpanInfo(
"<link1>",
"</link1>",
new NoUnderlineClickableSpan(
getContext(), this::onFledgeSettingsLinkClicked)),
new SpanApplier.SpanInfo(
"<link2>",
"</link2>",
new NoUnderlineClickableSpan(
getContext(), this::onCookieSettingsLink)),
new SpanApplier.SpanInfo(
"<link3>",
"</link3>",
new NoUnderlineClickableSpan(
getContext(), this::onManagingAdPrivacyClicked))));
}
@Override
public ObservableSupplier<String> getPageTitle() {
return mPageTitle;
}
private void onLearnMoreClicked(View view) {
RecordUserAction.record("Settings.PrivacySandbox.Topics.LearnMoreClicked");
launchSettingsActivity(TopicsLearnMoreFragment.class);
}
private void onManagingAdPrivacyClicked(View view) {
openUrlInCct(PrivacySandboxSettingsFragment.HELP_CENTER_URL);
}
private void onFledgeSettingsLinkClicked(View view) {
launchSettingsActivity(FledgeFragment.class);
}
private void onCookieSettingsLink(View view) {
launchCookieSettings();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Disable animations of preference changes.
getListView().setItemAnimator(null);
}
@Override
public void onResume() {
super.onResume();
populateCurrentTopics();
updatePreferenceVisibility();
}
@Override
public boolean onPreferenceChange(@NonNull Preference preference, Object value) {
if (preference.getKey().equals(TOPICS_TOGGLE_PREFERENCE)) {
boolean enabled = (boolean) value;
RecordUserAction.record(
enabled
? "Settings.PrivacySandbox.Topics.Enabled"
: "Settings.PrivacySandbox.Topics.Disabled");
setTopicsPrefEnabled(getProfile(), enabled);
updatePreferenceVisibility();
getPrivacySandboxBridge().topicsToggleChanged(enabled);
return true;
}
return false;
}
@Override
public boolean onPreferenceClick(@NonNull Preference preference) {
if (preference instanceof TopicPreference) {
getPrivacySandboxBridge()
.setTopicAllowed(((TopicPreference) preference).getTopic(), false);
mCurrentTopicsCategory.removePreference(preference);
updatePreferenceVisibility();
showSnackbar(
R.string.settings_topics_page_block_topic_snackbar,
null,
Snackbar.TYPE_ACTION,
Snackbar.UMA_PRIVACY_SANDBOX_REMOVE_INTEREST,
/* actionStringResId= */ 0,
/* multiLine= */ true);
RecordUserAction.record("Settings.PrivacySandbox.Topics.TopicRemoved");
return true;
}
return false;
}
private void populateCurrentTopics() {
mCurrentTopicsCategory.removeAll();
List<Topic> currentTopics = getPrivacySandboxBridge().getCurrentTopTopics();
for (Topic topic : currentTopics) {
TopicPreference preference = new TopicPreference(getContext(), topic);
preference.setImage(
R.drawable.btn_close,
getResources()
.getString(
R.string.privacy_sandbox_remove_interest_button_description,
topic.getName()));
preference.setDividerAllowedAbove(false);
preference.setOnPreferenceClickListener(this);
mCurrentTopicsCategory.addPreference(preference);
}
}
private void updatePreferenceVisibility() {
boolean topicsEnabled = isTopicsPrefEnabled(getProfile());
boolean topicsEmpty = mCurrentTopicsCategory.getPreferenceCount() == 0;
// Visible when Topics are disabled.
mDisabledTopicsPreference.setVisible(
!topicsEnabled
&& !ChromeFeatureList.isEnabled(
ChromeFeatureList.PRIVACY_SANDBOX_PROACTIVE_TOPICS_BLOCKING));
// Visible when Topics are enabled, but the current Topics list is empty.
mEmptyTopicsPreference.setVisible(topicsEnabled && topicsEmpty);
// Visible when Topics are enabled and the current Topics list is not empty.
mCurrentTopicsCategory.setVisible(topicsEnabled && !topicsEmpty);
// The new UI hides all the sections when the Topics are disabled.
if (ChromeFeatureList.isEnabled(
ChromeFeatureList.PRIVACY_SANDBOX_PROACTIVE_TOPICS_BLOCKING)) {
mActiveTopicsPreference.setVisible(topicsEnabled);
mBlockedTopicsPreference.setVisible(topicsEnabled);
mManageTopicsPreference.setVisible(topicsEnabled);
}
}
private ChromeManagedPreferenceDelegate createManagedPreferenceDelegate() {
return new ChromeManagedPreferenceDelegate(getProfile()) {
@Override
public boolean isPreferenceControlledByPolicy(Preference preference) {
if (TOPICS_TOGGLE_PREFERENCE.equals(preference.getKey())) {
return isTopicsPrefManaged(getProfile());
}
return false;
}
};
}
}