Bug 1123107 - Include Reading List checkbox during account creation. r=rnewman

This is a reasonable-sized refactoring underneath a small feature-patch.
The refactor reworks what information we maintain (and pickle) about
"enabled" services.  We've moved from a boolean "Sync enabled" flag to a
map of Android authorities (which map to services like Firefox Sync and
reading list) and boolean flags indicating whether each authority is
"automatically synced".  The checkboxes in the status activity
correspond directly to whether the authority (service) is automatically
synced.

The set of authorities we care about is determined by the DEFAULT_* map.
Said map is interrogated and written to the pickle file at Sync time.
When the pickle file is un-pickled, only the set of known authorities is
acted upon.  What that means is that both writing and reading a pickle
file with different sets of authorities should work across upgrades: if
a known authority is missing, the default value will be used; if an
unknown authority is present, it will be ignored.  This lets us alter
the set of known authorities via the build flag.

I have tested and Android maintains the "sync automatically" state for
an authority even when the authority is not present in the list of sync
checkboxes.

All in all, I'm not concerned about toggling the build flag multiple
times in the future.  (If we backed out the updated pickling code, we
would need to handle pickle downgrades, but we already needed to handle
that.)

========

fc8936549a
Author: Nick Alexander <nalexander@mozilla.com>
Date:   Wed Feb 11 10:37:34 2015 -0800

    Bug 1123107 - Part 3: Include Reading List checkbox during account creation.

    We are careful to show the checkbox only when the reading list service
    is enabled.

========

c90ea353cc
Author: Nick Alexander <nalexander@mozilla.com>
Date:   Wed Feb 11 10:31:15 2015 -0800

    Bug 1123107 - Part 2: Thread authorities to sync automatically through sign up/sign in flow.

========

e0c4d20744
Author: Nick Alexander <nalexander@mozilla.com>
Date:   Mon Feb 9 12:35:15 2015 -0800

    Bug 1123107 - Part 1: Manage map of automatically synced authorities.

========

7f7e308190
Author: Nick Alexander <nalexander@mozilla.com>
Date:   Mon Feb 9 11:54:54 2015 -0800

    Bug 1123107 - Part 0: Remove "account needs to be enabled" warning in status activity.

    As we move Sync to a model where a status checkbox sets whether a single
    ContentProvider is synced, it no longer makes sense to message when the
    "account" is not automatically syncing.
This commit is contained in:
Nick Alexander 2015-02-11 12:12:31 -08:00
parent 130ed4d90f
commit 93d39d6ddf
13 changed files with 180 additions and 119 deletions

View File

@ -26,6 +26,7 @@ import android.accounts.AccountManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
/**
@ -274,7 +275,7 @@ public class FirefoxAccounts {
ThreadPool.run(new Runnable() {
@Override
public void run() {
for (String authority : AndroidFxAccount.getAndroidAuthorities()) {
for (String authority : AndroidFxAccount.DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP.keySet()) {
ContentResolver.requestSync(account, authority, extras);
}
}

View File

@ -308,12 +308,13 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
public final PasswordStretcher passwordStretcher;
public final String serverURI;
public final Map<String, Boolean> selectedEngines;
public final Map<String, Boolean> authoritiesToSyncAutomaticallyMap;
public AddAccountDelegate(String email, PasswordStretcher passwordStretcher, String serverURI) {
this(email, passwordStretcher, serverURI, null);
this(email, passwordStretcher, serverURI, null, AndroidFxAccount.DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP);
}
public AddAccountDelegate(String email, PasswordStretcher passwordStretcher, String serverURI, Map<String, Boolean> selectedEngines) {
public AddAccountDelegate(String email, PasswordStretcher passwordStretcher, String serverURI, Map<String, Boolean> selectedEngines, Map<String, Boolean> authoritiesToSyncAutomaticallyMap) {
if (email == null) {
throw new IllegalArgumentException("email must not be null");
}
@ -323,6 +324,9 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
if (serverURI == null) {
throw new IllegalArgumentException("serverURI must not be null");
}
if (authoritiesToSyncAutomaticallyMap == null) {
throw new IllegalArgumentException("authoritiesToSyncAutomaticallyMap must not be null");
}
this.email = email;
this.passwordStretcher = passwordStretcher;
this.serverURI = serverURI;
@ -330,6 +334,8 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
// userSelectedEngines to prefs. This makes any created meta/global record
// have the default set of engines to sync.
this.selectedEngines = selectedEngines;
// authoritiesToSyncAutomaticallymap cannot be null.
this.authoritiesToSyncAutomaticallyMap = authoritiesToSyncAutomaticallyMap;
}
@Override
@ -357,7 +363,8 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
profile,
serverURI,
tokenServerURI,
state);
state,
this.authoritiesToSyncAutomaticallyMap);
if (fxAccount == null) {
throw new RuntimeException("Could not add Android account.");
}

View File

@ -12,6 +12,7 @@ import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountAgeLockoutHelper;
@ -22,6 +23,8 @@ import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
import org.mozilla.gecko.background.fxa.FxAccountUtils;
import org.mozilla.gecko.background.fxa.PasswordStretcher;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.tasks.FxAccountCreateAccountTask;
import org.mozilla.gecko.sync.Utils;
@ -62,6 +65,8 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
protected View monthDaycombo;
protected Map<String, Boolean> selectedEngines;
protected final Map<String, Boolean> authoritiesToSyncAutomaticallyMap =
new HashMap<String, Boolean>(AndroidFxAccount.DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP);
/**
* {@inheritDoc}
@ -363,12 +368,12 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
dayEdit.setTag(null);
}
public void createAccount(String email, String password, Map<String, Boolean> engines) {
public void createAccount(String email, String password, Map<String, Boolean> engines, Map<String, Boolean> authoritiesToSyncAutomaticallyMap) {
String serverURI = getAuthServerEndpoint();
PasswordStretcher passwordStretcher = makePasswordStretcher(password);
// This delegate creates a new Android account on success, opens the
// appropriate "success!" activity, and finishes this activity.
RequestDelegate<LoginResponse> delegate = new AddAccountDelegate(email, passwordStretcher, serverURI, engines) {
RequestDelegate<LoginResponse> delegate = new AddAccountDelegate(email, passwordStretcher, serverURI, engines, authoritiesToSyncAutomaticallyMap) {
@Override
public void handleError(Exception e) {
showRemoteError(e, R.string.fxaccount_create_account_unknown_error);
@ -413,9 +418,13 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
final Map<String, Boolean> engines = chooseCheckBox.isChecked()
? selectedEngines
: null;
// Only include authorities if the user currently has the option checked.
final Map<String, Boolean> authoritiesMap = chooseCheckBox.isChecked()
? authoritiesToSyncAutomaticallyMap
: AndroidFxAccount.DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP;
if (FxAccountAgeLockoutHelper.passesAgeCheck(dayOfBirth, zeroBasedMonthOfBirth, yearEdit.getText().toString(), yearItems)) {
FxAccountUtils.pii(LOG_TAG, "Passed age check.");
createAccount(email, password, engines);
createAccount(email, password, engines, authoritiesMap);
} else {
FxAccountUtils.pii(LOG_TAG, "Failed age check!");
FxAccountAgeLockoutHelper.lockOut(SystemClock.elapsedRealtime());
@ -435,7 +444,13 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
final int INDEX_HISTORY = 1;
final int INDEX_TABS = 2;
final int INDEX_PASSWORDS = 3;
final int NUMBER_OF_ENGINES = 4;
final int INDEX_READING_LIST = 4; // Only valid if reading list is enabled.
final int NUMBER_OF_ENGINES;
if (AppConstants.MOZ_ANDROID_READING_LIST_SERVICE) {
NUMBER_OF_ENGINES = 5;
} else {
NUMBER_OF_ENGINES = 4;
}
final String items[] = new String[NUMBER_OF_ENGINES];
final boolean checkedItems[] = new boolean[NUMBER_OF_ENGINES];
@ -443,6 +458,9 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
items[INDEX_HISTORY] = getResources().getString(R.string.fxaccount_status_history);
items[INDEX_TABS] = getResources().getString(R.string.fxaccount_status_tabs);
items[INDEX_PASSWORDS] = getResources().getString(R.string.fxaccount_status_passwords);
if (AppConstants.MOZ_ANDROID_READING_LIST_SERVICE) {
items[INDEX_READING_LIST] = getResources().getString(R.string.fxaccount_status_reading_list);
}
// Default to everything checked.
for (int i = 0; i < NUMBER_OF_ENGINES; i++) {
checkedItems[i] = true;
@ -468,7 +486,11 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
selectedEngines.put("history", checkedItems[INDEX_HISTORY]);
selectedEngines.put("tabs", checkedItems[INDEX_TABS]);
selectedEngines.put("passwords", checkedItems[INDEX_PASSWORDS]);
if (AppConstants.MOZ_ANDROID_READING_LIST_SERVICE) {
authoritiesToSyncAutomaticallyMap.put(BrowserContract.READING_LIST_AUTHORITY, checkedItems[INDEX_READING_LIST]);
}
FxAccountUtils.pii(LOG_TAG, "Updating selectedEngines: " + selectedEngines.toString());
FxAccountUtils.pii(LOG_TAG, "Updating authorities: " + authoritiesToSyncAutomaticallyMap.toString());
}
};

View File

@ -78,7 +78,6 @@ public class FxAccountStatusFragment
protected Preference needsUpgradePreference;
protected Preference needsVerificationPreference;
protected Preference needsMasterSyncAutomaticallyEnabledPreference;
protected Preference needsAccountEnabledPreference;
protected Preference needsFinishMigratingPreference;
protected PreferenceCategory syncCategory;
@ -143,7 +142,6 @@ public class FxAccountStatusFragment
needsUpgradePreference = ensureFindPreference("needs_upgrade");
needsVerificationPreference = ensureFindPreference("needs_verification");
needsMasterSyncAutomaticallyEnabledPreference = ensureFindPreference("needs_master_sync_automatically_enabled");
needsAccountEnabledPreference = ensureFindPreference("needs_account_enabled");
needsFinishMigratingPreference = ensureFindPreference("needs_finish_migrating");
syncCategory = (PreferenceCategory) ensureFindPreference("sync_category");
@ -163,7 +161,6 @@ public class FxAccountStatusFragment
needsPasswordPreference.setOnPreferenceClickListener(this);
needsVerificationPreference.setOnPreferenceClickListener(this);
needsAccountEnabledPreference.setOnPreferenceClickListener(this);
needsFinishMigratingPreference.setOnPreferenceClickListener(this);
bookmarksPreference.setOnPreferenceClickListener(this);
@ -238,13 +235,6 @@ public class FxAccountStatusFragment
return true;
}
if (preference == needsAccountEnabledPreference) {
fxAccount.enableSyncing();
refresh();
return true;
}
if (preference == bookmarksPreference ||
preference == historyPreference ||
preference == passwordsPreference ||
@ -301,7 +291,6 @@ public class FxAccountStatusFragment
this.needsUpgradePreference,
this.needsVerificationPreference,
this.needsMasterSyncAutomaticallyEnabledPreference,
this.needsAccountEnabledPreference,
this.needsFinishMigratingPreference,
};
for (Preference errorPreference : errorPreferences) {
@ -345,12 +334,6 @@ public class FxAccountStatusFragment
setCheckboxesEnabled(false);
}
protected void showNeedsAccountEnabled() {
syncCategory.setTitle(R.string.fxaccount_status_sync);
showOnlyOneErrorPreference(needsAccountEnabledPreference);
setCheckboxesEnabled(false);
}
protected void showNeedsFinishMigrating() {
syncCategory.setTitle(R.string.fxaccount_status_sync);
showOnlyOneErrorPreference(needsFinishMigratingPreference);
@ -478,17 +461,10 @@ public class FxAccountStatusFragment
try {
// There are error states determined by Android, not the login state
// machine, and we have a chance to present these states here. We handle
// machine, and we have a chance to present these states here. We handle
// them specially, since we can't surface these states as part of syncing,
// because they generally stop syncs from happening regularly.
// The action to enable syncing the Firefox Account doesn't require
// leaving this activity, so let's present it first.
final boolean isSyncing = fxAccount.isSyncing();
if (!isSyncing) {
showNeedsAccountEnabled();
return;
}
// because they generally stop syncs from happening regularly. Right now
// there are no such states.
// Interrogate the Firefox Account's state.
State state = fxAccount.getState();

View File

@ -8,8 +8,12 @@ import java.io.FileOutputStream;
import java.io.PrintStream;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.login.State.StateLabel;
@ -54,7 +58,7 @@ import android.content.Context;
public class AccountPickler {
public static final String LOG_TAG = AccountPickler.class.getSimpleName();
public static final long PICKLE_VERSION = 2;
public static final long PICKLE_VERSION = 3;
public static final String KEY_PICKLE_VERSION = "pickle_version";
public static final String KEY_PICKLE_TIMESTAMP = "pickle_timestamp";
@ -65,6 +69,10 @@ public class AccountPickler {
public static final String KEY_PROFILE = "profile";
public static final String KEY_IDP_SERVER_URI = "idpServerURI";
public static final String KEY_TOKEN_SERVER_URI = "tokenServerURI";
public static final String KEY_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP = "authoritiesToSyncAutomaticallyMap";
// Deprecated, but maintained for migration purposes.
public static final String KEY_IS_SYNCING_ENABLED = "isSyncingEnabled";
public static final String KEY_BUNDLE = "bundle";
@ -91,7 +99,12 @@ public class AccountPickler {
o.put(KEY_PROFILE, account.getProfile());
o.put(KEY_IDP_SERVER_URI, account.getAccountServerURI());
o.put(KEY_TOKEN_SERVER_URI, account.getTokenServerURI());
o.put(KEY_IS_SYNCING_ENABLED, account.isSyncing());
final ExtendedJSONObject p = new ExtendedJSONObject();
for (Entry<String, Boolean> pair : account.getAuthoritiesToSyncAutomaticallyMap().entrySet()) {
p.put(pair.getKey(), pair.getValue());
}
o.put(KEY_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP, p);
// TODO: If prefs version changes under us, SyncPrefsPath will change, "clearing" prefs.
@ -173,8 +186,10 @@ public class AccountPickler {
final AndroidFxAccount account;
try {
account = AndroidFxAccount.addAndroidAccount(context, params.email, params.profile,
params.idpServerURI, params.tokenServerURI, params.state, params.accountVersion,
params.isSyncingEnabled, true, params.bundle);
params.authServerURI, params.tokenServerURI, params.state,
params.authoritiesToSyncAutomaticallyMap,
params.accountVersion,
true, params.bundle);
} catch (Exception e) {
Logger.warn(LOG_TAG, "Exception when adding Android Account; aborting.", e);
return null;
@ -203,9 +218,9 @@ public class AccountPickler {
private int accountVersion;
private String email;
private String profile;
private String idpServerURI;
private String authServerURI;
private String tokenServerURI;
private boolean isSyncingEnabled;
private final Map<String, Boolean> authoritiesToSyncAutomaticallyMap = new HashMap<>();
private ExtendedJSONObject bundle;
private State state;
@ -226,32 +241,47 @@ public class AccountPickler {
* internal Android Account type has changed. Version 1 used to throw in
* this case, but we intentionally used the pickle file to migrate across
* Account types, bumping the version simultaneously.
*
* Version 3 replaces "isSyncEnabled" with a map (String -> Boolean)
* associating Android authorities to whether or not they are configured
* to sync automatically.
*/
switch (params.pickleVersion.intValue()) {
case 2: {
// Sanity check.
final String accountType = json.getString(KEY_ACCOUNT_TYPE);
if (!FxAccountConstants.ACCOUNT_TYPE.equals(accountType)) {
throw new IllegalStateException("Account type has changed from " + accountType + " to " + FxAccountConstants.ACCOUNT_TYPE + ".");
}
params.unpickleV1(json);
case 3: {
// Sanity check.
final String accountType = json.getString(KEY_ACCOUNT_TYPE);
if (!FxAccountConstants.ACCOUNT_TYPE.equals(accountType)) {
throw new IllegalStateException("Account type has changed from " + accountType + " to " + FxAccountConstants.ACCOUNT_TYPE + ".");
}
break;
case 1: {
// Warn about account type changing, but don't throw over it.
final String accountType = json.getString(KEY_ACCOUNT_TYPE);
if (!FxAccountConstants.ACCOUNT_TYPE.equals(accountType)) {
Logger.warn(LOG_TAG, "Account type has changed from " + accountType + " to " + FxAccountConstants.ACCOUNT_TYPE + "; ignoring.");
}
params.unpickleV3(json);
}
break;
params.unpickleV1(json);
case 2: {
// Sanity check.
final String accountType = json.getString(KEY_ACCOUNT_TYPE);
if (!FxAccountConstants.ACCOUNT_TYPE.equals(accountType)) {
throw new IllegalStateException("Account type has changed from " + accountType + " to " + FxAccountConstants.ACCOUNT_TYPE + ".");
}
break;
default:
throw new IllegalStateException("Unknown pickle version, " + params.pickleVersion + ".");
params.unpickleV1(json);
}
break;
case 1: {
// Warn about account type changing, but don't throw over it.
final String accountType = json.getString(KEY_ACCOUNT_TYPE);
if (!FxAccountConstants.ACCOUNT_TYPE.equals(accountType)) {
Logger.warn(LOG_TAG, "Account type has changed from " + accountType + " to " + FxAccountConstants.ACCOUNT_TYPE + "; ignoring.");
}
params.unpickleV1(json);
}
break;
default:
throw new IllegalStateException("Unknown pickle version, " + params.pickleVersion + ".");
}
return params;
@ -263,9 +293,11 @@ public class AccountPickler {
this.accountVersion = json.getIntegerSafely(KEY_ACCOUNT_VERSION);
this.email = json.getString(KEY_EMAIL);
this.profile = json.getString(KEY_PROFILE);
this.idpServerURI = json.getString(KEY_IDP_SERVER_URI);
this.authServerURI = json.getString(KEY_IDP_SERVER_URI);
this.tokenServerURI = json.getString(KEY_TOKEN_SERVER_URI);
this.isSyncingEnabled = json.getBoolean(KEY_IS_SYNCING_ENABLED);
// We get the default value for everything except syncing browser data.
this.authoritiesToSyncAutomaticallyMap.put(BrowserContract.AUTHORITY, json.getBoolean(KEY_IS_SYNCING_ENABLED));
this.bundle = json.getObject(KEY_BUNDLE);
if (bundle == null) {
@ -274,6 +306,25 @@ public class AccountPickler {
this.state = getState(bundle);
}
private void unpickleV3(final ExtendedJSONObject json)
throws NonObjectJSONException, NoSuchAlgorithmException, InvalidKeySpecException {
// We'll overwrite the extracted sync automatically map.
unpickleV1(json);
// Extract the map of authorities to sync automatically.
authoritiesToSyncAutomaticallyMap.clear();
final ExtendedJSONObject o = json.getObject(KEY_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP);
if (o == null) {
return;
}
for (String key : o.keySet()) {
final Boolean enabled = o.getBoolean(key);
if (enabled != null) {
authoritiesToSyncAutomaticallyMap.put(key, enabled);
}
}
}
private State getState(final ExtendedJSONObject bundle) throws InvalidKeySpecException,
NonObjectJSONException, NoSuchAlgorithmException {
// TODO: Should copy-pasta BUNDLE_KEY_STATE & LABEL to this file to ensure we maintain

View File

@ -8,11 +8,12 @@ import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.background.common.GlobalConstants;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountUtils;
@ -64,7 +65,17 @@ public class AndroidFxAccount {
public static final String BUNDLE_KEY_STATE_LABEL = "stateLabel";
public static final String BUNDLE_KEY_STATE = "state";
protected static final List<String> ANDROID_AUTHORITIES = Collections.unmodifiableList(Arrays.asList(BrowserContract.AUTHORITY));
public static final Map<String, Boolean> DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP;
static {
final HashMap<String, Boolean> m = new HashMap<String, Boolean>();
// By default, Firefox Sync is enabled.
m.put(BrowserContract.AUTHORITY, true);
if (AppConstants.MOZ_ANDROID_READING_LIST_SERVICE) {
// Sync the Reading List.
m.put(BrowserContract.READING_LIST_AUTHORITY, true);
}
DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP = Collections.unmodifiableMap(m);
}
private static final String PREF_KEY_LAST_SYNCED_TIMESTAMP = "lastSyncedTimestamp";
@ -318,10 +329,12 @@ public class AndroidFxAccount {
String profile,
String idpServerURI,
String tokenServerURI,
State state)
State state,
final Map<String, Boolean> authoritiesToSyncAutomaticallyMap)
throws UnsupportedEncodingException, GeneralSecurityException, URISyntaxException {
return addAndroidAccount(context, email, profile, idpServerURI, tokenServerURI, state,
CURRENT_ACCOUNT_VERSION, true, false, null);
authoritiesToSyncAutomaticallyMap,
CURRENT_ACCOUNT_VERSION, false, null);
}
public static AndroidFxAccount addAndroidAccount(
@ -331,8 +344,8 @@ public class AndroidFxAccount {
String idpServerURI,
String tokenServerURI,
State state,
final Map<String, Boolean> authoritiesToSyncAutomaticallyMap,
final int accountVersion,
final boolean syncEnabled,
final boolean fromPickle,
ExtendedJSONObject bundle)
throws UnsupportedEncodingException, GeneralSecurityException, URISyntaxException {
@ -402,11 +415,7 @@ public class AndroidFxAccount {
fxAccount.clearSyncPrefs();
}
if (syncEnabled) {
fxAccount.enableSyncing();
} else {
fxAccount.disableSyncing();
}
fxAccount.setAuthoritiesToSyncAutomaticallyMap(authoritiesToSyncAutomaticallyMap);
return fxAccount;
}
@ -415,40 +424,31 @@ public class AndroidFxAccount {
getSyncPrefs().edit().clear().commit();
}
public static Iterable<String> getAndroidAuthorities() {
return ANDROID_AUTHORITIES;
}
/**
* Return true if the underlying Android account is currently set to sync automatically.
* <p>
* This is, confusingly, not the same thing as "being syncable": that refers
* to whether this account can be synced, ever; this refers to whether Android
* will try to sync the account at appropriate times.
*
* @return true if the account is set to sync automatically.
*/
public boolean isSyncing() {
boolean isSyncEnabled = true;
for (String authority : getAndroidAuthorities()) {
isSyncEnabled &= ContentResolver.getSyncAutomatically(account, authority);
public void setAuthoritiesToSyncAutomaticallyMap(Map<String, Boolean> authoritiesToSyncAutomaticallyMap) {
if (authoritiesToSyncAutomaticallyMap == null) {
throw new IllegalArgumentException("authoritiesToSyncAutomaticallyMap must not be null");
}
return isSyncEnabled;
}
public void enableSyncing() {
Logger.info(LOG_TAG, "Enabling sync for account named like " + getObfuscatedEmail());
for (String authority : getAndroidAuthorities()) {
ContentResolver.setSyncAutomatically(account, authority, true);
for (String authority : DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP.keySet()) {
boolean authorityEnabled = DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP.get(authority);
final Boolean enabled = authoritiesToSyncAutomaticallyMap.get(authority);
if (enabled != null) {
authorityEnabled = enabled.booleanValue();
}
// Accounts are always capable of being synced ...
ContentResolver.setIsSyncable(account, authority, 1);
// ... but not always automatically synced.
ContentResolver.setSyncAutomatically(account, authority, authorityEnabled);
}
}
public void disableSyncing() {
Logger.info(LOG_TAG, "Disabling sync for account named like " + getObfuscatedEmail());
for (String authority : getAndroidAuthorities()) {
ContentResolver.setSyncAutomatically(account, authority, false);
public Map<String, Boolean> getAuthoritiesToSyncAutomaticallyMap() {
final Map<String, Boolean> authoritiesToSync = new HashMap<>();
for (String authority : DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP.keySet()) {
final boolean enabled = ContentResolver.getSyncAutomatically(account, authority);
authoritiesToSync.put(authority, enabled);
}
return authoritiesToSync;
}
/**
@ -458,7 +458,7 @@ public class AndroidFxAccount {
*/
public boolean isCurrentlySyncing() {
boolean active = false;
for (String authority : AndroidFxAccount.getAndroidAuthorities()) {
for (String authority : AndroidFxAccount.DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP.keySet()) {
active |= ContentResolver.isSyncActive(account, authority);
}
return active;

View File

@ -209,7 +209,6 @@
<!ENTITY fxaccount_status_needs_upgrade 'You need to upgrade &brandShortName; to sign in.'>
<!ENTITY fxaccount_status_needs_master_sync_automatically_enabled '&syncBrand.shortName.label; is set up, but not syncing automatically. Toggle “Auto-sync data” in Android Settings &gt; Data Usage.'>
<!ENTITY fxaccount_status_needs_master_sync_automatically_enabled_v21 '&syncBrand.shortName.label; is set up, but not syncing automatically. Toggle “Auto-sync data” in the menu of Android Settings &gt; Accounts.'>
<!ENTITY fxaccount_status_needs_account_enabled '&syncBrand.shortName.label; is set up, but not syncing automatically. Tap to start syncing.'>
<!ENTITY fxaccount_status_needs_finish_migrating 'Tap to sign in to your new Firefox Account.'>
<!ENTITY fxaccount_status_bookmarks 'Bookmarks'>
<!ENTITY fxaccount_status_history 'History'>

View File

@ -47,13 +47,6 @@
android:layout="@layout/fxaccount_status_error_preference"
android:persistent="false"
android:title="@string/fxaccount_status_needs_master_sync_automatically_enabled" />
<Preference
android:editable="false"
android:icon="@drawable/fxaccount_sync_error"
android:key="needs_account_enabled"
android:layout="@layout/fxaccount_status_error_preference"
android:persistent="false"
android:title="@string/fxaccount_status_needs_account_enabled" />
<Preference
android:editable="false"
android:icon="@drawable/fxaccount_sync_error"

View File

@ -80,7 +80,8 @@ public class MigrationSentinelSyncStage extends AbstractNonRepositorySyncStage {
profile,
authServerURI,
tokenServerURI,
state);
state,
AndroidFxAccount.DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP);
if (fxAccount == null) {
Logger.warn(LOG_TAG, "Could not add Android account named like: " + Utils.obfuscateEmail(email));

View File

@ -199,12 +199,12 @@
<string name="fxaccount_status_needs_upgrade">&fxaccount_status_needs_upgrade;</string>
<string name="fxaccount_status_needs_master_sync_automatically_enabled">&fxaccount_status_needs_master_sync_automatically_enabled;</string>
<string name="fxaccount_status_needs_master_sync_automatically_enabled_v21">&fxaccount_status_needs_master_sync_automatically_enabled_v21;</string>
<string name="fxaccount_status_needs_account_enabled">&fxaccount_status_needs_account_enabled;</string>
<string name="fxaccount_status_needs_finish_migrating">&fxaccount_status_needs_finish_migrating;</string>
<string name="fxaccount_status_bookmarks">&fxaccount_status_bookmarks;</string>
<string name="fxaccount_status_history">&fxaccount_status_history;</string>
<string name="fxaccount_status_passwords">&fxaccount_status_passwords;</string>
<string name="fxaccount_status_tabs">&fxaccount_status_tabs;</string>
<string name="fxaccount_status_reading_list">&reading_list_title;</string>
<string name="fxaccount_status_legal">&fxaccount_status_legal;</string>
<string name="fxaccount_status_linktos">&fxaccount_status_linktos;</string>
<string name="fxaccount_status_linkprivacy">&fxaccount_status_linkprivacy;</string>

View File

@ -175,7 +175,8 @@ public class TestAccountLoader extends AndroidSyncTestCaseWithAccounts {
// This account will get cleaned up in tearDown.
final State state = new Separated(TEST_USERNAME, "uid", false); // State choice is arbitrary.
final AndroidFxAccount account = AndroidFxAccount.addAndroidAccount(context,
TEST_USERNAME, TEST_PROFILE, TEST_AUTH_SERVER_URI, TEST_TOKEN_SERVER_URI, state);
TEST_USERNAME, TEST_PROFILE, TEST_AUTH_SERVER_URI, TEST_TOKEN_SERVER_URI, state,
AndroidSyncTestCaseWithAccounts.TEST_SYNC_AUTOMATICALLY_MAP_WITH_ALL_AUTHORITIES_DISABLED);
assertNotNull(account);
assertFirefoxAccount(getLoaderResultSynchronously(loader));
}

View File

@ -55,7 +55,8 @@ public class TestAccountPickler extends AndroidSyncTestCaseWithAccounts {
public AndroidFxAccount addTestAccount() throws Exception {
final State state = new Separated(TEST_USERNAME, "uid", false); // State choice is arbitrary.
final AndroidFxAccount account = AndroidFxAccount.addAndroidAccount(context, TEST_USERNAME,
TEST_PROFILE, TEST_AUTH_SERVER_URI, TEST_TOKEN_SERVER_URI, state);
TEST_PROFILE, TEST_AUTH_SERVER_URI, TEST_TOKEN_SERVER_URI, state,
AndroidSyncTestCaseWithAccounts.TEST_SYNC_AUTOMATICALLY_MAP_WITH_ALL_AUTHORITIES_DISABLED);
assertNotNull(account);
assertNotNull(account.getProfile());
assertTrue(testAccountsExist()); // Sanity check.
@ -65,13 +66,12 @@ public class TestAccountPickler extends AndroidSyncTestCaseWithAccounts {
public void testPickle() throws Exception {
final AndroidFxAccount account = addTestAccount();
// Sync is enabled by default so we do a more thorough test by disabling it.
account.disableSyncing();
final long now = System.currentTimeMillis();
final ExtendedJSONObject o = AccountPickler.toJSON(account, now);
assertNotNull(o.toJSONString());
assertEquals(2, o.getLong(AccountPickler.KEY_PICKLE_VERSION).longValue());
assertEquals(3, o.getLong(AccountPickler.KEY_PICKLE_VERSION).longValue());
assertTrue(o.getLong(AccountPickler.KEY_PICKLE_TIMESTAMP).longValue() < System.currentTimeMillis());
assertEquals(AndroidFxAccount.CURRENT_ACCOUNT_VERSION, o.getIntegerSafely(AccountPickler.KEY_ACCOUNT_VERSION).intValue());
@ -82,14 +82,12 @@ public class TestAccountPickler extends AndroidSyncTestCaseWithAccounts {
assertEquals(TEST_AUTH_SERVER_URI, o.getString(AccountPickler.KEY_IDP_SERVER_URI));
assertEquals(TEST_TOKEN_SERVER_URI, o.getString(AccountPickler.KEY_TOKEN_SERVER_URI));
assertFalse(o.getBoolean(AccountPickler.KEY_IS_SYNCING_ENABLED));
assertNotNull(o.getObject(AccountPickler.KEY_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP));
assertNotNull(o.get(AccountPickler.KEY_BUNDLE));
}
public void testPickleAndUnpickle() throws Exception {
final AndroidFxAccount inputAccount = addTestAccount();
// Sync is enabled by default so we do a more thorough test by disabling it.
inputAccount.disableSyncing();
AccountPickler.pickle(inputAccount, PICKLE_FILENAME);
final ExtendedJSONObject inputJSON = AccountPickler.toJSON(inputAccount, 0);

View File

@ -6,9 +6,12 @@ package org.mozilla.gecko.background.sync;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import android.accounts.Account;
import android.accounts.AccountManager;
@ -22,6 +25,15 @@ public class AndroidSyncTestCaseWithAccounts extends AndroidSyncTestCase {
protected AccountManager accountManager;
protected int numAccounts;
public static final Map<String, Boolean> TEST_SYNC_AUTOMATICALLY_MAP_WITH_ALL_AUTHORITIES_DISABLED;
static {
final Map<String, Boolean> m = new HashMap<String, Boolean>();
for (String authority : AndroidFxAccount.DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP.keySet()) {
m.put(authority, false);
}
TEST_SYNC_AUTOMATICALLY_MAP_WITH_ALL_AUTHORITIES_DISABLED = m;
}
public AndroidSyncTestCaseWithAccounts(String accountType, String accountPrefix) {
super();
this.testAccountType = accountType;