chromium/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentBasic.java

// Copyright 2017 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.browsing_data;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Browser;
import android.text.SpannableString;
import android.view.View;

import androidx.annotation.IntDef;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.preference.Preference;

import org.chromium.base.Callback;
import org.chromium.base.IntentUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
import org.chromium.chrome.browser.sync.SyncServiceFactory;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.document.ChromeAsyncTabLauncher;
import org.chromium.components.browser_ui.settings.ClickableSpansTextMessagePreference;
import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.components.search_engines.TemplateUrl;
import org.chromium.components.search_engines.TemplateUrlService;
import org.chromium.components.signin.identitymanager.ConsentLevel;
import org.chromium.components.signin.identitymanager.IdentityManager;
import org.chromium.components.sync.DataType;
import org.chromium.components.sync.SyncService;
import org.chromium.ui.text.NoUnderlineClickableSpan;
import org.chromium.ui.text.SpanApplier;
import org.chromium.ui.text.SpanApplier.SpanInfo;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;

/**
 * A simpler version of {@link ClearBrowsingDataFragment} with fewer dialog options and more
 * explanatory text.
 */
public class ClearBrowsingDataFragmentBasic extends ClearBrowsingDataFragment {
    /**
     * Functional interface to start a Chrome Custom Tab for the given intent, e.g. by using {@link
     * org.chromium.chrome.browser.LaunchIntentDispatcher#createCustomTabActivityIntent}.
     * TODO(crbug.com/40773797): Update when LaunchIntentDispatcher is (partially-)modularized.
     */
    public interface CustomTabIntentHelper {
        /**
         * @see org.chromium.chrome.browser.LaunchIntentDispatcher#createCustomTabActivityIntent
         */
        Intent createCustomTabActivityIntent(Context context, Intent intent);
    }

    /**
     * UMA histogram values for MyActivity navigations.
     * Note: this should stay in sync with ClearBrowsingDataMyActivityNavigation in enums.xml.
     */
    @IntDef({MyActivityNavigation.TOP_LEVEL, MyActivityNavigation.SEARCH_HISTORY})
    @Retention(RetentionPolicy.SOURCE)
    private @interface MyActivityNavigation {
        int TOP_LEVEL = 0;
        int SEARCH_HISTORY = 1;
        int NUM_ENTRIES = 2;
    }

    private CustomTabIntentHelper mCustomTabHelper;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ClearBrowsingDataCheckBoxPreference historyCheckbox =
                (ClearBrowsingDataCheckBoxPreference)
                        findPreference(getPreferenceKey(DialogOption.CLEAR_HISTORY));
        ClearBrowsingDataCheckBoxPreference cookiesCheckbox =
                (ClearBrowsingDataCheckBoxPreference)
                        findPreference(getPreferenceKey(DialogOption.CLEAR_COOKIES_AND_SITE_DATA));

        historyCheckbox.setLinkClickDelegate(
                () -> {
                    new ChromeAsyncTabLauncher(/* incognito= */ false)
                            .launchUrl(
                                    UrlConstants.MY_ACTIVITY_URL_IN_CBD,
                                    TabLaunchType.FROM_CHROME_UI);
                });

        IdentityManager identityManager =
                IdentityServicesProvider.get().getIdentityManager(getProfile());
        if (identityManager.hasPrimaryAccount(ConsentLevel.SIGNIN)) {
            // Update the Clear Browsing History text based on the sign-in/sync state and whether
            // the link to MyActivity is displayed inline or at the bottom of the page.
            // Note: when  sync is disabled, the default string is used.
            if (isHistorySyncEnabled()) {
                // The text is different only for users with history sync.
                historyCheckbox.setSummary(R.string.clear_browsing_history_summary_synced_no_link);
            }
            cookiesCheckbox.setSummary(
                    R.string.clear_cookies_and_site_data_summary_basic_signed_in);
        }
    }

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        super.onCreatePreferences(savedInstanceState, rootKey);
        Profile profile = getProfile();
        IdentityManager identityManager =
                IdentityServicesProvider.get().getIdentityManager(profile);
        ClickableSpansTextMessagePreference googleDataTextPref =
                (ClickableSpansTextMessagePreference)
                        findPreference(ClearBrowsingDataFragment.PREF_GOOGLE_DATA_TEXT);
        Preference nonGoogleSearchHistoryTextPref =
                findPreference(ClearBrowsingDataFragment.PREF_SEARCH_HISTORY_NON_GOOGLE_TEXT);
        TemplateUrlService templateUrlService = TemplateUrlServiceFactory.getForProfile(profile);
        TemplateUrl defaultSearchEngine = templateUrlService.getDefaultSearchEngineTemplateUrl();
        boolean isDefaultSearchEngineGoogle = templateUrlService.isDefaultSearchEngineGoogle();

        // Google-related links to delete search history and other browsing activity.
        if (defaultSearchEngine == null
                || !identityManager.hasPrimaryAccount(ConsentLevel.SIGNIN)) {
            // One of two cases:
            // 1. The default search engine is disabled.
            // 2. The user is not signed into Chrome.
            // In all those cases, delete the link to clear Google data using MyActivity.
            deleteGoogleDataTextIfExists();
        } else if (isDefaultSearchEngineGoogle) {
            // Signed-in and the DSE is Google. Build the text with two links.
            googleDataTextPref.setSummary(buildGoogleSearchHistoryText());
        } else {
            // Signed-in and non-Google DSE. Build the text with the MyActivity link only.
            googleDataTextPref.setSummary(buildGoogleMyActivityText());
        }

        // Text for search history if DSE is not Google.
        if (defaultSearchEngine == null || isDefaultSearchEngineGoogle) {
            // One of two cases:
            // 1. The default search engine is disabled.
            // 2. The default search engine is Google.
            // In all those cases, delete the link to clear non-Google search history.
            deleteNonGoogleSearchHistoryTextIfExists();
        } else if (defaultSearchEngine.getIsPrepopulated()) {
            // Prepopulated non-Google DSE. Use its name in the text.
            nonGoogleSearchHistoryTextPref.setSummary(
                    getContext()
                            .getString(
                                    R.string.clear_search_history_non_google_dse,
                                    defaultSearchEngine.getShortName()));
        } else {
            // Unknown non-Google DSE. Use generic text.
            nonGoogleSearchHistoryTextPref.setSummary(
                    R.string.clear_search_history_non_google_dse_unknown);
        }
    }

    public void setCustomTabIntentHelper(CustomTabIntentHelper tabHelper) {
        mCustomTabHelper = tabHelper;
    }

    private void deleteGoogleDataTextIfExists() {
        Preference googleDataTextPref =
                findPreference(ClearBrowsingDataFragment.PREF_GOOGLE_DATA_TEXT);
        if (googleDataTextPref != null) {
            getPreferenceScreen().removePreference(googleDataTextPref);
        }
    }

    private void deleteNonGoogleSearchHistoryTextIfExists() {
        Preference searchHistoryTextPref =
                findPreference(ClearBrowsingDataFragment.PREF_SEARCH_HISTORY_NON_GOOGLE_TEXT);
        if (searchHistoryTextPref != null) {
            getPreferenceScreen().removePreference(searchHistoryTextPref);
        }
    }

    private SpannableString buildGoogleSearchHistoryText() {
        return SpanApplier.applySpans(
                getContext().getString(R.string.clear_search_history_link),
                new SpanInfo(
                        "<link1>",
                        "</link1>",
                        new NoUnderlineClickableSpan(
                                getContext(),
                                createOpenMyActivityCallback(/* openSearchHistory= */ true))),
                new SpanInfo(
                        "<link2>",
                        "</link2>",
                        new NoUnderlineClickableSpan(
                                getContext(),
                                createOpenMyActivityCallback(/* openSearchHistory= */ false))));
    }

    private SpannableString buildGoogleMyActivityText() {
        return SpanApplier.applySpans(
                getContext().getString(R.string.clear_search_history_link_other_forms),
                new SpanInfo(
                        "<link1>",
                        "</link1>",
                        new NoUnderlineClickableSpan(
                                getContext(),
                                createOpenMyActivityCallback(/* openSearchHistory= */ false))));
    }

    /** If openSearchHistory is true, opens the search history page; otherwise: top level. */
    private Callback<View> createOpenMyActivityCallback(boolean openSearchHistory) {
        return (widget) -> {
            assert mCustomTabHelper != null
                    : "CCT helper must be set on ClearBrowsingFragmentBasic before opening a link.";
            CustomTabsIntent customTabIntent =
                    new CustomTabsIntent.Builder().setShowTitle(true).build();

            String url;
            if (openSearchHistory) {
                url = UrlConstants.GOOGLE_SEARCH_HISTORY_URL_IN_CBD;
                RecordHistogram.recordEnumeratedHistogram(
                        "Settings.ClearBrowsingData.OpenMyActivity",
                        MyActivityNavigation.SEARCH_HISTORY,
                        MyActivityNavigation.NUM_ENTRIES);
            } else {
                url = UrlConstants.MY_ACTIVITY_URL_IN_CBD;
                RecordHistogram.recordEnumeratedHistogram(
                        "Settings.ClearBrowsingData.OpenMyActivity",
                        MyActivityNavigation.TOP_LEVEL,
                        MyActivityNavigation.NUM_ENTRIES);
            }
            customTabIntent.intent.setData(Uri.parse(url));
            Intent intent =
                    mCustomTabHelper.createCustomTabActivityIntent(
                            getContext(), customTabIntent.intent);
            intent.setPackage(getContext().getPackageName());
            intent.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
            IntentUtils.addTrustedIntentExtras(intent);
            IntentUtils.safeStartActivity(getContext(), intent);
        };
    }

    private boolean isHistorySyncEnabled() {
        SyncService syncService = SyncServiceFactory.getForProfile(getProfile());
        return syncService != null
                && syncService.getActiveDataTypes().contains(DataType.HISTORY_DELETE_DIRECTIVES);
    }

    @Override
    protected int getClearBrowsingDataTabType() {
        return ClearBrowsingDataTab.BASIC;
    }

    @Override
    protected List<Integer> getDialogOptions(Bundle fragmentArgs) {
        return Arrays.asList(
                DialogOption.CLEAR_HISTORY,
                DialogOption.CLEAR_COOKIES_AND_SITE_DATA,
                DialogOption.CLEAR_CACHE);
    }

    @Override
    protected void onClearBrowsingData() {
        super.onClearBrowsingData();
        RecordHistogram.recordEnumeratedHistogram(
                "History.ClearBrowsingData.UserDeletedFromTab",
                ClearBrowsingDataTab.BASIC,
                ClearBrowsingDataTab.MAX_VALUE + 1);
        RecordUserAction.record("ClearBrowsingData_BasicTab");
    }
}