blob: 34acde3520ed1e939c6712aa9b4f90bb465dd5d6 [file] [log] [blame]
// Copyright 2018 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;
import android.accounts.Account;
import android.app.DialogFragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ListView;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.BuildInfo;
import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.autofill.PersonalDataManager;
import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial;
import org.chromium.chrome.browser.help.HelpAndFeedback;
import org.chromium.chrome.browser.invalidation.InvalidationController;
import org.chromium.chrome.browser.preferences.privacy.PrivacyPreferencesManager;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.signin.SigninManager;
import org.chromium.chrome.browser.signin.SigninUtils;
import org.chromium.chrome.browser.sync.GoogleServiceAuthError;
import org.chromium.chrome.browser.sync.ProfileSyncService;
import org.chromium.chrome.browser.sync.ui.PassphraseCreationDialogFragment;
import org.chromium.chrome.browser.sync.ui.PassphraseDialogFragment;
import org.chromium.chrome.browser.sync.ui.PassphraseTypeDialogFragment;
import org.chromium.components.signin.AccountManagerFacade;
import org.chromium.components.signin.ChromeSigninController;
import org.chromium.components.sync.AndroidSyncSettings;
import org.chromium.components.sync.ModelType;
import org.chromium.components.sync.PassphraseType;
import org.chromium.components.sync.ProtocolErrorClientAction;
import org.chromium.components.sync.StopSource;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashSet;
import java.util.Set;
/**
* Settings fragment to customize Sync options (data types, encryption) and other services that
* communicate with Google.
*/
public class SyncAndServicesPreferences extends PreferenceFragment
implements PassphraseDialogFragment.Listener, PassphraseCreationDialogFragment.Listener,
PassphraseTypeDialogFragment.Listener, Preference.OnPreferenceChangeListener,
Preference.OnPreferenceClickListener,
ProfileSyncService.SyncStateChangedListener {
private static final String USE_SYNC_AND_ALL_SERVICES = "use_sync_and_all_services";
private static final String SYNC_AND_PERSONALIZATION = "sync_and_personalization";
private static final String NONPERSONALIZED_SERVICES = "nonpersonalized_services";
private static final String PREF_NAVIGATION_ERROR = "navigation_error";
private static final String PREF_SEARCH_SUGGESTIONS = "search_suggestions";
private static final String PREF_SAFE_BROWSING_EXTENDED_REPORTING =
"safe_browsing_extended_reporting";
private static final String PREF_SAFE_BROWSING_SCOUT_REPORTING =
"safe_browsing_scout_reporting";
private static final String PREF_SAFE_BROWSING = "safe_browsing";
private static final String PREF_CONTEXTUAL_SEARCH = "contextual_search";
private static final String PREF_NETWORK_PREDICTIONS = "network_predictions";
private static final String PREF_USAGE_AND_CRASH_REPORTING = "usage_and_crash_reports";
@VisibleForTesting
public static final String FRAGMENT_ENTER_PASSPHRASE = "enter_password";
@VisibleForTesting
public static final String FRAGMENT_CUSTOM_PASSPHRASE = "custom_password";
@VisibleForTesting
public static final String FRAGMENT_PASSPHRASE_TYPE = "password_type";
@VisibleForTesting
public static final String PREFERENCE_SYNC_AUTOFILL = "sync_autofill";
@VisibleForTesting
public static final String PREFERENCE_SYNC_BOOKMARKS = "sync_bookmarks";
@VisibleForTesting
public static final String PREFERENCE_SYNC_OMNIBOX = "sync_omnibox";
@VisibleForTesting
public static final String PREFERENCE_SYNC_PASSWORDS = "sync_passwords";
@VisibleForTesting
public static final String PREFERENCE_SYNC_RECENT_TABS = "sync_recent_tabs";
@VisibleForTesting
public static final String PREFERENCE_SYNC_SETTINGS = "sync_settings";
@VisibleForTesting
public static final String PREFERENCE_PAYMENTS_INTEGRATION = "payments_integration";
@VisibleForTesting
public static final String PREFERENCE_ENCRYPTION = "encryption";
@VisibleForTesting
public static final String PREFERENCE_SYNC_MANAGE_DATA = "sync_manage_data";
public static final String PREFERENCE_SYNC_ERROR_CARD = "sync_error_card";
@IntDef({SyncError.NO_ERROR, SyncError.ANDROID_SYNC_DISABLED, SyncError.AUTH_ERROR,
SyncError.PASSPHRASE_REQUIRED, SyncError.CLIENT_OUT_OF_DATE, SyncError.OTHER_ERRORS})
@Retention(RetentionPolicy.SOURCE)
private @interface SyncError {
int NO_ERROR = -1;
int ANDROID_SYNC_DISABLED = 0;
int AUTH_ERROR = 1;
int PASSPHRASE_REQUIRED = 2;
int CLIENT_OUT_OF_DATE = 3;
int OTHER_ERRORS = 128;
}
private static final String DASHBOARD_URL = "https://www.google.com/settings/chrome/sync";
private final ProfileSyncService mProfileSyncService = ProfileSyncService.get();
private final PrefServiceBridge mPrefServiceBridge = PrefServiceBridge.getInstance();
private final PrivacyPreferencesManager mPrivacyPrefManager =
PrivacyPreferencesManager.getInstance();
private final ManagedPreferenceDelegate mManagedPreferenceDelegate =
createManagedPreferenceDelegate();
private ChromeSwitchPreference mUseSyncAndAllServices;
private SigninExpandablePreferenceGroup mSyncGroup;
private CheckBoxPreference mSyncAutofill;
private CheckBoxPreference mSyncBookmarks;
private CheckBoxPreference mSyncOmnibox;
private CheckBoxPreference mSyncPasswords;
private CheckBoxPreference mSyncRecentTabs;
private CheckBoxPreference mSyncSettings;
private CheckBoxPreference mPaymentsIntegration;
// Contains preferences for all sync data types.
private CheckBoxPreference[] mAllTypes;
private Preference mSyncEncryption;
private Preference mManageSyncData;
private Preference mSyncErrorCard;
private SigninExpandablePreferenceGroup mNonpersonalizedServices;
private ChromeBaseCheckBoxPreference mNavigationError;
private ChromeBaseCheckBoxPreference mNetworkPredictions;
private ChromeBaseCheckBoxPreference mSearchSuggestions;
private Preference mContextualSearch;
private ChromeBaseCheckBoxPreference mSafeBrowsingReporting;
private ChromeBaseCheckBoxPreference mSafeBrowsing;
private Preference mUsageAndCrashReporting;
private boolean mIsEngineInitialized;
private boolean mIsPassphraseRequired;
/**
* This is usually equal to AndroidSyncSettings.isSyncEnabled(), but may have a different value
* if passphrase dialog is shown (see {@link #onStop} for details).
*/
private boolean mIsSyncEnabled;
private @SyncError int mCurrentSyncError = SyncError.NO_ERROR;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPrivacyPrefManager.migrateNetworkPredictionPreferences();
getActivity().setTitle(R.string.prefs_sync_and_services);
setHasOptionsMenu(true);
PreferenceUtils.addPreferencesFromResource(this, R.xml.sync_and_services_preferences);
mUseSyncAndAllServices = (ChromeSwitchPreference) findPreference(USE_SYNC_AND_ALL_SERVICES);
mUseSyncAndAllServices.setOnPreferenceChangeListener(this);
mSyncGroup = (SigninExpandablePreferenceGroup) findPreference(SYNC_AND_PERSONALIZATION);
mNonpersonalizedServices =
(SigninExpandablePreferenceGroup) findPreference(NONPERSONALIZED_SERVICES);
mSyncAutofill = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_AUTOFILL);
mSyncBookmarks = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_BOOKMARKS);
mSyncOmnibox = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_OMNIBOX);
mSyncPasswords = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_PASSWORDS);
mSyncRecentTabs = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_RECENT_TABS);
mSyncSettings = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_SETTINGS);
mPaymentsIntegration = (CheckBoxPreference) findPreference(PREFERENCE_PAYMENTS_INTEGRATION);
mSyncEncryption = findPreference(PREFERENCE_ENCRYPTION);
mSyncEncryption.setOnPreferenceClickListener(this);
mManageSyncData = findPreference(PREFERENCE_SYNC_MANAGE_DATA);
mManageSyncData.setOnPreferenceClickListener(this);
mSyncErrorCard = findPreference(PREFERENCE_SYNC_ERROR_CARD);
mSyncErrorCard.setOnPreferenceClickListener(this);
mAllTypes = new CheckBoxPreference[] {mSyncAutofill, mSyncBookmarks, mSyncOmnibox,
mSyncPasswords, mSyncRecentTabs, mSyncSettings, mPaymentsIntegration};
for (CheckBoxPreference type : mAllTypes) {
type.setOnPreferenceChangeListener(this);
}
mNetworkPredictions =
(ChromeBaseCheckBoxPreference) findPreference(PREF_NETWORK_PREDICTIONS);
mNetworkPredictions.setOnPreferenceChangeListener(this);
mNetworkPredictions.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
mNavigationError = (ChromeBaseCheckBoxPreference) findPreference(PREF_NAVIGATION_ERROR);
mNavigationError.setOnPreferenceChangeListener(this);
mNavigationError.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
mSearchSuggestions = (ChromeBaseCheckBoxPreference) findPreference(PREF_SEARCH_SUGGESTIONS);
mSearchSuggestions.setOnPreferenceChangeListener(this);
mSearchSuggestions.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
mContextualSearch = findPreference(PREF_CONTEXTUAL_SEARCH);
if (!ContextualSearchFieldTrial.isEnabled()) {
removePreference(mNonpersonalizedServices, mContextualSearch);
mContextualSearch = null;
}
Preference extendedReporting = findPreference(PREF_SAFE_BROWSING_EXTENDED_REPORTING);
Preference scoutReporting = findPreference(PREF_SAFE_BROWSING_SCOUT_REPORTING);
// Remove the extended reporting preference that is NOT active.
if (mPrefServiceBridge.isSafeBrowsingScoutReportingActive()) {
removePreference(mNonpersonalizedServices, extendedReporting);
mSafeBrowsingReporting = (ChromeBaseCheckBoxPreference) scoutReporting;
} else {
removePreference(mNonpersonalizedServices, scoutReporting);
mSafeBrowsingReporting = (ChromeBaseCheckBoxPreference) extendedReporting;
}
mSafeBrowsingReporting.setOnPreferenceChangeListener(this);
mSafeBrowsingReporting.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
mSafeBrowsing = (ChromeBaseCheckBoxPreference) findPreference(PREF_SAFE_BROWSING);
mSafeBrowsing.setOnPreferenceChangeListener(this);
mSafeBrowsing.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
mUsageAndCrashReporting = findPreference(PREF_USAGE_AND_CRASH_REPORTING);
updatePreferences();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ListView list = (ListView) getView().findViewById(android.R.id.list);
list.setDivider(null);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
MenuItem help =
menu.add(Menu.NONE, R.id.menu_id_targeted_help, Menu.NONE, R.string.menu_help);
help.setIcon(R.drawable.ic_help_and_feedback);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.menu_id_targeted_help) {
HelpAndFeedback.getInstance(getActivity())
.show(getActivity(), getString(R.string.help_context_privacy),
Profile.getLastUsedProfile(), null);
return true;
}
return false;
}
@Override
public void onStart() {
super.onStart();
mIsEngineInitialized = mProfileSyncService.isEngineInitialized();
mIsPassphraseRequired =
mIsEngineInitialized && mProfileSyncService.isPassphraseRequiredForDecryption();
// This prevents sync from actually syncing until the dialog is closed.
mProfileSyncService.setSetupInProgress(true);
mProfileSyncService.addSyncStateChangedListener(this);
updateSyncStateFromAndroidSyncSettings();
}
@Override
public void onStop() {
super.onStop();
mProfileSyncService.removeSyncStateChangedListener(this);
// If this activity is closing, apply configuration changes and tell sync that
// the user is done configuring sync.
if (getActivity().isChangingConfigurations()) return;
// Only save state if internal and external state match. If a stop and clear comes
// while the dialog is open, this will be false and settings won't be saved.
if (mIsSyncEnabled && AndroidSyncSettings.isSyncEnabled()) {
// Save the new data type state.
configureSyncDataTypes();
// Inform sync that the user has finished setting up sync at least once.
mProfileSyncService.setFirstSetupComplete();
}
PersonalDataManager.setPaymentsIntegrationEnabled(mPaymentsIntegration.isChecked());
// Setup is done. This was preventing sync from turning on even if it was enabled.
// TODO(crbug/557784): This needs to be set only when we think the user is done with
// setting up. This means: 1) If the user leaves the Sync Settings screen (via back)
// or, 2) If the user leaves the screen by tapping on "Manage Synced Data"
mProfileSyncService.setSetupInProgress(false);
}
@Override
public void onResume() {
super.onResume();
updatePreferences();
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String key = preference.getKey();
if (USE_SYNC_AND_ALL_SERVICES.equals(key)) {
if ((boolean) newValue) {
mSyncGroup.setExpanded(true);
mNonpersonalizedServices.setExpanded(true);
}
ThreadUtils.postOnUiThread(this::updateSyncStateFromSelectedModelTypes);
ThreadUtils.postOnUiThread(this::updateDataTypeState);
} else if (PREF_SEARCH_SUGGESTIONS.equals(key)) {
PrefServiceBridge.getInstance().setSearchSuggestEnabled((boolean) newValue);
} else if (PREF_SAFE_BROWSING.equals(key)) {
PrefServiceBridge.getInstance().setSafeBrowsingEnabled((boolean) newValue);
} else if (PREF_SAFE_BROWSING_EXTENDED_REPORTING.equals(key)
|| PREF_SAFE_BROWSING_SCOUT_REPORTING.equals(key)) {
PrefServiceBridge.getInstance().setSafeBrowsingExtendedReportingEnabled(
(boolean) newValue);
} else if (PREF_NETWORK_PREDICTIONS.equals(key)) {
PrefServiceBridge.getInstance().setNetworkPredictionEnabled((boolean) newValue);
recordNetworkPredictionEnablingUMA((boolean) newValue);
} else if (PREF_NAVIGATION_ERROR.equals(key)) {
PrefServiceBridge.getInstance().setResolveNavigationErrorEnabled((boolean) newValue);
} else if (isSyncTypePreference(preference)) {
final boolean syncAutofillToggled = preference == mSyncAutofill;
final boolean preferenceChecked = (boolean) newValue;
ThreadUtils.postOnUiThread(() -> {
if (syncAutofillToggled) {
// If the user checks the autofill sync checkbox, then enable and check the
// payments integration checkbox.
//
// If the user unchecks the autofill sync checkbox, then disable and uncheck
// the payments integration checkbox.
mPaymentsIntegration.setEnabled(preferenceChecked);
mPaymentsIntegration.setChecked(preferenceChecked);
}
updateSyncStateFromSelectedModelTypes();
});
}
return true;
}
/** Callback for OnPreferenceClickListener */
@Override
public boolean onPreferenceClick(Preference preference) {
if (!isResumed()) {
// This event could come in after onPause if the user clicks back and the preference at
// roughly the same time. See http://b/5983282
return false;
}
if (preference == mSyncEncryption && mProfileSyncService.isEngineInitialized()) {
if (mProfileSyncService.isPassphraseRequiredForDecryption()) {
displayPassphraseDialog();
} else {
displayPassphraseTypeDialog();
return true;
}
} else if (preference == mManageSyncData) {
openDashboardTabInNewActivityStack();
return true;
} else if (preference == mSyncErrorCard) {
onSyncErrorCardClicked();
return true;
}
return false;
}
/**
* ProfileSyncService.SyncStateChangedListener implementation, listens to sync state changes.
*
* If the user has just turned on sync, this listener is needed in order to enable
* the encryption settings once the engine has initialized.
*/
@Override
public void syncStateChanged() {
boolean wasSyncInitialized = mIsEngineInitialized;
boolean wasPassphraseRequired = mIsPassphraseRequired;
mIsEngineInitialized = mProfileSyncService.isEngineInitialized();
mIsPassphraseRequired =
mIsEngineInitialized && mProfileSyncService.isPassphraseRequiredForDecryption();
if (mIsEngineInitialized != wasSyncInitialized
|| mIsPassphraseRequired != wasPassphraseRequired) {
// Update all because Password syncability is also affected by the engine.
updateSyncPreferences();
} else {
updateSyncErrorCard();
}
}
/** Returns whether Sync can be disabled. */
private boolean canDisableSync() {
return !Profile.getLastUsedProfile().isChild();
}
private boolean isSyncTypePreference(Preference preference) {
for (Preference pref : mAllTypes) {
if (pref == preference) return true;
}
return false;
}
/**
* Update the state of all settings from sync.
*
* This sets the state of the sync switch from external sync state and then calls
* updateSyncPreferences, which uses that as its source of truth.
*/
private void updateSyncStateFromAndroidSyncSettings() {
mIsSyncEnabled = AndroidSyncSettings.isSyncEnabled();
updateSyncPreferences();
}
/** Update sync preferences using mIsSyncEnabled to determine if sync is enabled. */
private void updateSyncPreferences() {
updateDataTypeState();
updateEncryptionState();
updateSyncErrorCard();
}
/** Enables sync if any of the data types is selected, otherwise disables sync. */
private void updateSyncStateFromSelectedModelTypes() {
boolean shouldEnableSync = mUseSyncAndAllServices.isChecked()
|| !getSelectedModelTypes().isEmpty() || !canDisableSync();
if (mIsSyncEnabled == shouldEnableSync) return;
mIsSyncEnabled = shouldEnableSync;
if (shouldEnableSync) {
mProfileSyncService.requestStart();
} else {
stopSync();
}
updateSyncPreferences();
}
/**
* Update the encryption state.
*
* If sync's engine is initialized, the button is enabled and the dialog will present the
* valid encryption options for the user. Otherwise, any encryption dialogs will be closed
* and the button will be disabled because the engine is needed in order to know and
* modify the encryption state.
*/
private void updateEncryptionState() {
mSyncEncryption.setEnabled(mIsSyncEnabled && mIsEngineInitialized);
mSyncEncryption.setSummary(null);
if (!mIsEngineInitialized) {
// If sync is not initialized, encryption state is unavailable and can't be changed.
// Leave the button disabled and the summary empty. Additionally, close the dialogs in
// case they were open when a stop and clear comes.
closeDialogIfOpen(FRAGMENT_CUSTOM_PASSPHRASE);
closeDialogIfOpen(FRAGMENT_ENTER_PASSPHRASE);
return;
}
if (!mProfileSyncService.isPassphraseRequiredForDecryption()) {
closeDialogIfOpen(FRAGMENT_ENTER_PASSPHRASE);
}
if (mProfileSyncService.isPassphraseRequiredForDecryption() && isAdded()) {
mSyncEncryption.setSummary(
errorSummary(getString(R.string.sync_need_passphrase), getActivity()));
}
}
/** Applies a span to the given string to give it an error color. */
private static Spannable errorSummary(String string, Context context) {
SpannableString summary = new SpannableString(string);
summary.setSpan(new ForegroundColorSpan(ApiCompatibilityUtils.getColor(
context.getResources(), R.color.input_underline_error_color)),
0, summary.length(), 0);
return summary;
}
private void configureSyncDataTypes() {
updateSyncStateFromSelectedModelTypes();
if (!mIsSyncEnabled) return;
boolean syncEverything = mUseSyncAndAllServices.isChecked();
mProfileSyncService.setPreferredDataTypes(syncEverything, getSelectedModelTypes());
// Update the invalidation listener with the set of types we are enabling.
InvalidationController invController = InvalidationController.get();
invController.ensureStartedAndUpdateRegisteredTypes();
}
private Set<Integer> getSelectedModelTypes() {
Set<Integer> types = new HashSet<>();
if (mSyncAutofill.isChecked()) types.add(ModelType.AUTOFILL);
if (mSyncBookmarks.isChecked()) types.add(ModelType.BOOKMARKS);
if (mSyncOmnibox.isChecked()) types.add(ModelType.TYPED_URLS);
if (mSyncPasswords.isChecked()) types.add(ModelType.PASSWORDS);
if (mSyncRecentTabs.isChecked()) types.add(ModelType.PROXY_TABS);
if (mSyncSettings.isChecked()) types.add(ModelType.PREFERENCES);
return types;
}
private void displayPassphraseTypeDialog() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
PassphraseTypeDialogFragment dialog =
PassphraseTypeDialogFragment.create(mProfileSyncService.getPassphraseType(),
mProfileSyncService.getExplicitPassphraseTime(),
mProfileSyncService.isEncryptEverythingAllowed());
dialog.show(ft, FRAGMENT_PASSPHRASE_TYPE);
dialog.setTargetFragment(this, -1);
}
private void displayPassphraseDialog() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
PassphraseDialogFragment.newInstance(this).show(ft, FRAGMENT_ENTER_PASSPHRASE);
}
private void displayCustomPassphraseDialog() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
PassphraseCreationDialogFragment dialog = new PassphraseCreationDialogFragment();
dialog.setTargetFragment(this, -1);
dialog.show(ft, FRAGMENT_CUSTOM_PASSPHRASE);
}
private void closeDialogIfOpen(String tag) {
FragmentManager manager = getFragmentManager();
if (manager == null) {
// Do nothing if the manager doesn't exist yet; see http://crbug.com/480544.
return;
}
DialogFragment df = (DialogFragment) manager.findFragmentByTag(tag);
if (df != null) {
df.dismiss();
}
}
/** Returns whether the passphrase successfully decrypted the pending keys. */
private boolean handleDecryption(String passphrase) {
if (passphrase.isEmpty() || !mProfileSyncService.setDecryptionPassphrase(passphrase)) {
return false;
}
// PassphraseDialogFragment doesn't handle closing itself, so do it here. This is not done
// in updateSyncStateFromAndroidSyncSettings() because that happens onResume and possibly in
// other cases where the dialog should stay open.
closeDialogIfOpen(FRAGMENT_ENTER_PASSPHRASE);
// Update our configuration UI.
updateSyncStateFromAndroidSyncSettings();
return true;
}
/** Callback for PassphraseDialogFragment.Listener */
@Override
public boolean onPassphraseEntered(String passphrase) {
if (!mProfileSyncService.isEngineInitialized()) {
// If the engine was shut down since the dialog was opened, do nothing.
return false;
}
return handleDecryption(passphrase);
}
/** Callback for PassphraseDialogFragment.Listener */
@Override
public void onPassphraseCanceled() {}
/** Callback for PassphraseCreationDialogFragment.Listener */
@Override
public void onPassphraseCreated(String passphrase) {
if (!mProfileSyncService.isEngineInitialized()) {
// If the engine was shut down since the dialog was opened, do nothing.
return;
}
mProfileSyncService.enableEncryptEverything();
mProfileSyncService.setEncryptionPassphrase(passphrase);
// Configure the current set of data types - this tells the sync engine to
// apply our encryption configuration changes.
configureSyncDataTypes();
// Re-display our config UI to properly reflect the new state.
updateSyncStateFromAndroidSyncSettings();
}
/** Callback for PassphraseTypeDialogFragment.Listener */
@Override
public void onPassphraseTypeSelected(PassphraseType type) {
if (!mProfileSyncService.isEngineInitialized()) {
// If the engine was shut down since the dialog was opened, do nothing.
return;
}
boolean isAllDataEncrypted = mProfileSyncService.isEncryptEverythingEnabled();
boolean isUsingSecondaryPassphrase = mProfileSyncService.isUsingSecondaryPassphrase();
// The passphrase type should only ever be selected if the account doesn't have
// full encryption enabled. Otherwise both options should be disabled.
assert !isAllDataEncrypted;
assert !isUsingSecondaryPassphrase;
displayCustomPassphraseDialog();
}
/** Opens the Google Dashboard where the user can control the data stored for the account. */
private void openDashboardTabInNewActivityStack() {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(DASHBOARD_URL));
intent.setPackage(getActivity().getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
/**
* Update the data type switch state.
*
* If sync is on, load the prefs from native. Otherwise, all data types are disabled and
* checked. Note that the Password data type will be shown as disabled and unchecked between
* sync being turned on and the engine initialization completing.
*/
private void updateDataTypeState() {
boolean syncEverything = mUseSyncAndAllServices.isChecked();
boolean passwordSyncConfigurable = mProfileSyncService.isEngineInitialized()
&& mProfileSyncService.isCryptographerReady();
Set<Integer> syncTypes = mProfileSyncService.getPreferredDataTypes();
boolean syncAutofill = syncTypes.contains(ModelType.AUTOFILL);
for (CheckBoxPreference pref : mAllTypes) {
// Set the default state of each data type checkbox.
boolean canSyncType = true;
if (pref == mSyncPasswords) canSyncType = passwordSyncConfigurable;
if (pref == mPaymentsIntegration) {
canSyncType = syncAutofill || syncEverything;
}
if (syncEverything) {
pref.setChecked(canSyncType);
}
pref.setEnabled(!syncEverything && canSyncType);
}
if (mIsSyncEnabled && !syncEverything) {
// If "Use sync and all services" is off, check/uncheck the individual checkboxes
// to match the prefs.
mSyncAutofill.setChecked(syncAutofill);
mSyncBookmarks.setChecked(syncTypes.contains(ModelType.BOOKMARKS));
mSyncOmnibox.setChecked(syncTypes.contains(ModelType.TYPED_URLS));
mSyncPasswords.setChecked(
passwordSyncConfigurable && syncTypes.contains(ModelType.PASSWORDS));
mSyncRecentTabs.setChecked(syncTypes.contains(ModelType.PROXY_TABS));
mSyncSettings.setChecked(syncTypes.contains(ModelType.PREFERENCES));
mPaymentsIntegration.setChecked(
syncAutofill && PersonalDataManager.isPaymentsIntegrationEnabled());
}
}
private void updateSyncErrorCard() {
mCurrentSyncError = getSyncError();
if (mCurrentSyncError == SyncError.NO_ERROR) {
mSyncGroup.removePreference(mSyncErrorCard);
} else {
String summary = getSyncErrorHint(mCurrentSyncError);
mSyncErrorCard.setSummary(summary);
mSyncGroup.addPreference(mSyncErrorCard);
}
}
@SyncError
private int getSyncError() {
if (!AndroidSyncSettings.isMasterSyncEnabled()) {
return SyncError.ANDROID_SYNC_DISABLED;
}
if (!mIsSyncEnabled) {
return SyncError.NO_ERROR;
}
if (mProfileSyncService.getAuthError()
== GoogleServiceAuthError.State.INVALID_GAIA_CREDENTIALS) {
return SyncError.AUTH_ERROR;
}
if (mProfileSyncService.getProtocolErrorClientAction()
== ProtocolErrorClientAction.UPGRADE_CLIENT) {
return SyncError.CLIENT_OUT_OF_DATE;
}
if (mProfileSyncService.getAuthError() != GoogleServiceAuthError.State.NONE
|| mProfileSyncService.hasUnrecoverableError()) {
return SyncError.OTHER_ERRORS;
}
if (mProfileSyncService.isSyncActive()
&& mProfileSyncService.isPassphraseRequiredForDecryption()) {
return SyncError.PASSPHRASE_REQUIRED;
}
return SyncError.NO_ERROR;
}
/**
* Gets hint message to resolve sync error.
* @param error The sync error.
*/
private String getSyncErrorHint(@SyncError int error) {
Resources res = getActivity().getResources();
switch (error) {
case SyncError.ANDROID_SYNC_DISABLED:
return res.getString(R.string.hint_android_sync_disabled);
case SyncError.AUTH_ERROR:
return res.getString(R.string.hint_sync_auth_error);
case SyncError.CLIENT_OUT_OF_DATE:
return res.getString(
R.string.hint_client_out_of_date, BuildInfo.getInstance().hostPackageLabel);
case SyncError.OTHER_ERRORS:
return res.getString(R.string.hint_other_sync_errors);
case SyncError.PASSPHRASE_REQUIRED:
return res.getString(R.string.hint_passphrase_required);
case SyncError.NO_ERROR:
default:
return null;
}
}
private void onSyncErrorCardClicked() {
if (mCurrentSyncError == SyncError.NO_ERROR) {
return;
}
if (mCurrentSyncError == SyncError.ANDROID_SYNC_DISABLED) {
SigninUtils.openAccountSettingsPage(
getActivity(), ChromeSigninController.get().getSignedInAccountName());
return;
}
if (mCurrentSyncError == SyncError.AUTH_ERROR) {
AccountManagerFacade.get().updateCredentials(
ChromeSigninController.get().getSignedInUser(), getActivity(), null);
return;
}
if (mCurrentSyncError == SyncError.CLIENT_OUT_OF_DATE) {
// Opens the client in play store for update.
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id="
+ ContextUtils.getApplicationContext().getPackageName()));
startActivity(intent);
return;
}
if (mCurrentSyncError == SyncError.OTHER_ERRORS) {
final Account account = ChromeSigninController.get().getSignedInUser();
SigninManager.get().signOut(() -> SigninManager.get().signIn(account, null, null));
return;
}
if (mCurrentSyncError == SyncError.PASSPHRASE_REQUIRED) {
displayPassphraseDialog();
return;
}
}
private void stopSync() {
if (mProfileSyncService.isSyncRequested()) {
RecordHistogram.recordEnumeratedHistogram("Sync.StopSource",
StopSource.CHROME_SYNC_SETTINGS, StopSource.STOP_SOURCE_LIMIT);
mProfileSyncService.requestStop();
}
}
private void recordNetworkPredictionEnablingUMA(boolean enabled) {
// Report user turning on and off NetworkPrediction.
RecordHistogram.recordBooleanHistogram("PrefService.NetworkPredictionEnabled", enabled);
}
private static void removePreference(PreferenceGroup from, Preference preference) {
boolean found = from.removePreference(preference);
assert found : "Don't have such preference! Preference key: " + preference.getKey();
}
private void updatePreferences() {
mNavigationError.setChecked(mPrefServiceBridge.isResolveNavigationErrorEnabled());
mNetworkPredictions.setChecked(mPrefServiceBridge.getNetworkPredictionEnabled());
mSearchSuggestions.setChecked(mPrefServiceBridge.isSearchSuggestEnabled());
mSafeBrowsing.setChecked(mPrefServiceBridge.isSafeBrowsingEnabled());
mSafeBrowsingReporting.setChecked(
mPrefServiceBridge.isSafeBrowsingExtendedReportingEnabled());
CharSequence textOn = getActivity().getResources().getText(R.string.text_on);
CharSequence textOff = getActivity().getResources().getText(R.string.text_off);
if (mContextualSearch != null) {
boolean isContextualSearchEnabled = !mPrefServiceBridge.isContextualSearchDisabled();
mContextualSearch.setSummary(isContextualSearchEnabled ? textOn : textOff);
}
mUsageAndCrashReporting.setSummary(
mPrivacyPrefManager.isUsageAndCrashReportingPermittedByUser() ? textOn : textOff);
}
private ManagedPreferenceDelegate createManagedPreferenceDelegate() {
return preference -> {
String key = preference.getKey();
if (PREF_NAVIGATION_ERROR.equals(key)) {
return mPrefServiceBridge.isResolveNavigationErrorManaged();
}
if (PREF_SEARCH_SUGGESTIONS.equals(key)) {
return mPrefServiceBridge.isSearchSuggestManaged();
}
if (PREF_SAFE_BROWSING_EXTENDED_REPORTING.equals(key)
|| PREF_SAFE_BROWSING_SCOUT_REPORTING.equals(key)) {
return mPrefServiceBridge.isSafeBrowsingExtendedReportingManaged();
}
if (PREF_SAFE_BROWSING.equals(key)) {
return mPrefServiceBridge.isSafeBrowsingManaged();
}
if (PREF_NETWORK_PREDICTIONS.equals(key)) {
return mPrefServiceBridge.isNetworkPredictionManaged();
}
return false;
};
}
}