зеркало из https://github.com/mozilla/gecko-dev.git
Bug 965367 - Implement "Choose what to sync" check box. r=rnewman
This commit is contained in:
Родитель
694d6e0850
Коммит
cbe3deed81
|
@ -5,6 +5,7 @@
|
|||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
|
@ -18,6 +19,7 @@ import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.ProgressDisplay;
|
|||
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||
import org.mozilla.gecko.fxa.login.Engaged;
|
||||
import org.mozilla.gecko.fxa.login.State;
|
||||
import org.mozilla.gecko.sync.SyncConfiguration;
|
||||
import org.mozilla.gecko.sync.setup.Constants;
|
||||
|
||||
import android.accounts.AccountManager;
|
||||
|
@ -58,6 +60,7 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
|
|||
|
||||
protected void createShowPasswordButton() {
|
||||
showPasswordButton.setOnClickListener(new OnClickListener() {
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
boolean isShown = 0 == (passwordEdit.getInputType() & InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
|
@ -68,11 +71,11 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
|
|||
passwordEdit.setSelection(start, stop);
|
||||
if (isShown) {
|
||||
showPasswordButton.setText(R.string.fxaccount_password_show);
|
||||
showPasswordButton.setBackground(getResources().getDrawable(R.drawable.fxaccount_password_button_show_background));
|
||||
showPasswordButton.setBackgroundDrawable(getResources().getDrawable(R.drawable.fxaccount_password_button_show_background));
|
||||
showPasswordButton.setTextColor(getResources().getColor(R.color.fxaccount_password_show_textcolor));
|
||||
} else {
|
||||
showPasswordButton.setText(R.string.fxaccount_password_hide);
|
||||
showPasswordButton.setBackground(getResources().getDrawable(R.drawable.fxaccount_password_button_hide_background));
|
||||
showPasswordButton.setBackgroundDrawable(getResources().getDrawable(R.drawable.fxaccount_password_button_hide_background));
|
||||
showPasswordButton.setTextColor(getResources().getColor(R.color.fxaccount_password_hide_textcolor));
|
||||
}
|
||||
}
|
||||
|
@ -199,11 +202,29 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
|
|||
public final String email;
|
||||
public final PasswordStretcher passwordStretcher;
|
||||
public final String serverURI;
|
||||
public final Map<String, Boolean> selectedEngines;
|
||||
|
||||
public AddAccountDelegate(String email, PasswordStretcher passwordStretcher, String serverURI) {
|
||||
this(email, passwordStretcher, serverURI, null);
|
||||
}
|
||||
|
||||
public AddAccountDelegate(String email, PasswordStretcher passwordStretcher, String serverURI, Map<String, Boolean> selectedEngines) {
|
||||
if (email == null) {
|
||||
throw new IllegalArgumentException("email must not be null");
|
||||
}
|
||||
if (passwordStretcher == null) {
|
||||
throw new IllegalArgumentException("passwordStretcher must not be null");
|
||||
}
|
||||
if (serverURI == null) {
|
||||
throw new IllegalArgumentException("serverURI must not be null");
|
||||
}
|
||||
this.email = email;
|
||||
this.passwordStretcher = passwordStretcher;
|
||||
this.serverURI = serverURI;
|
||||
// selectedEngines can be null, which means don't write
|
||||
// userSelectedEngines to prefs. This makes any created meta/global record
|
||||
// have the default set of engines to sync.
|
||||
this.selectedEngines = selectedEngines;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -235,6 +256,11 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
|
|||
if (fxAccount == null) {
|
||||
throw new RuntimeException("Could not add Android account.");
|
||||
}
|
||||
|
||||
if (selectedEngines != null) {
|
||||
Logger.info(LOG_TAG, "User has selected engines; storing to prefs.");
|
||||
SyncConfiguration.storeSelectedEnginesToPrefs(fxAccount.getSyncPrefs(), selectedEngines);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleError(e);
|
||||
return;
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
|
@ -32,7 +34,9 @@ import android.os.SystemClock;
|
|||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
|
@ -46,6 +50,9 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
|
|||
|
||||
protected String[] yearItems;
|
||||
protected EditText yearEdit;
|
||||
protected CheckBox chooseCheckBox;
|
||||
|
||||
protected Map<String, Boolean> selectedEngines;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
|
@ -73,12 +80,15 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
|
|||
remoteErrorTextView = (TextView) ensureFindViewById(null, R.id.remote_error, "remote error text view");
|
||||
button = (Button) ensureFindViewById(null, R.id.button, "create account button");
|
||||
progressBar = (ProgressBar) ensureFindViewById(null, R.id.progress, "progress bar");
|
||||
chooseCheckBox = (CheckBox) ensureFindViewById(null, R.id.choose_what_to_sync_checkbox, "choose what to sync check box");
|
||||
selectedEngines = new HashMap<String, Boolean>();
|
||||
|
||||
createCreateAccountButton();
|
||||
createYearEdit();
|
||||
addListeners();
|
||||
updateButtonState();
|
||||
createShowPasswordButton();
|
||||
createChooseCheckBox();
|
||||
|
||||
View signInInsteadLink = ensureFindViewById(null, R.id.sign_in_instead_link, "sign in instead link");
|
||||
signInInsteadLink.setOnClickListener(new OnClickListener() {
|
||||
|
@ -147,12 +157,12 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
|
|||
});
|
||||
}
|
||||
|
||||
public void createAccount(String email, String password) {
|
||||
public void createAccount(String email, String password, Map<String, Boolean> engines) {
|
||||
String serverURI = FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT;
|
||||
PasswordStretcher passwordStretcher = new QuickPasswordStretcher(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) {
|
||||
RequestDelegate<LoginResponse> delegate = new AddAccountDelegate(email, passwordStretcher, serverURI, engines) {
|
||||
@Override
|
||||
public void handleError(Exception e) {
|
||||
showRemoteError(e, R.string.fxaccount_create_account_unknown_error);
|
||||
|
@ -189,9 +199,13 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
|
|||
}
|
||||
final String email = emailEdit.getText().toString();
|
||||
final String password = passwordEdit.getText().toString();
|
||||
// Only include selected engines if the user currently has the option checked.
|
||||
final Map<String, Boolean> engines = chooseCheckBox.isChecked()
|
||||
? selectedEngines
|
||||
: null;
|
||||
if (FxAccountAgeLockoutHelper.passesAgeCheck(yearEdit.getText().toString(), yearItems)) {
|
||||
FxAccountConstants.pii(LOG_TAG, "Passed age check.");
|
||||
createAccount(email, password);
|
||||
createAccount(email, password, engines);
|
||||
} else {
|
||||
FxAccountConstants.pii(LOG_TAG, "Failed age check!");
|
||||
FxAccountAgeLockoutHelper.lockOut(SystemClock.elapsedRealtime());
|
||||
|
@ -201,4 +215,91 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The "Choose what to sync" checkbox pops up a multi-choice dialog when it is
|
||||
* unchecked. It toggles to unchecked from checked.
|
||||
*/
|
||||
protected void createChooseCheckBox() {
|
||||
final int INDEX_BOOKMARKS = 0;
|
||||
final int INDEX_HISTORY = 1;
|
||||
final int INDEX_TABS = 2;
|
||||
final int INDEX_PASSWORDS = 3;
|
||||
final int NUMBER_OF_ENGINES = 4;
|
||||
|
||||
final String items[] = new String[NUMBER_OF_ENGINES];
|
||||
final boolean checkedItems[] = new boolean[NUMBER_OF_ENGINES];
|
||||
items[INDEX_BOOKMARKS] = getResources().getString(R.string.fxaccount_status_bookmarks);
|
||||
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);
|
||||
// Default to everything checked.
|
||||
for (int i = 0; i < NUMBER_OF_ENGINES; i++) {
|
||||
checkedItems[i] = true;
|
||||
}
|
||||
|
||||
final DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (which != DialogInterface.BUTTON_POSITIVE) {
|
||||
Logger.debug(LOG_TAG, "onClick: not button positive, unchecking.");
|
||||
chooseCheckBox.setChecked(false);
|
||||
return;
|
||||
}
|
||||
// We only check the box on success.
|
||||
Logger.debug(LOG_TAG, "onClick: button positive, checking.");
|
||||
chooseCheckBox.setChecked(true);
|
||||
// And then remember for future use.
|
||||
ListView selectionsList = ((AlertDialog) dialog).getListView();
|
||||
for (int i = 0; i < NUMBER_OF_ENGINES; i++) {
|
||||
checkedItems[i] = selectionsList.isItemChecked(i);
|
||||
}
|
||||
selectedEngines.put("bookmarks", checkedItems[INDEX_BOOKMARKS]);
|
||||
selectedEngines.put("history", checkedItems[INDEX_HISTORY]);
|
||||
selectedEngines.put("tabs", checkedItems[INDEX_TABS]);
|
||||
selectedEngines.put("passwords", checkedItems[INDEX_PASSWORDS]);
|
||||
FxAccountConstants.pii(LOG_TAG, "Updating selectedEngines: " + selectedEngines.toString());
|
||||
}
|
||||
};
|
||||
|
||||
final DialogInterface.OnMultiChoiceClickListener multiChoiceClickListener = new DialogInterface.OnMultiChoiceClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
|
||||
// Display multi-selection clicks in UI.
|
||||
ListView selectionsList = ((AlertDialog) dialog).getListView();
|
||||
selectionsList.setItemChecked(which, isChecked);
|
||||
}
|
||||
};
|
||||
|
||||
final AlertDialog dialog = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.fxaccount_create_account_choose_what_to_sync)
|
||||
.setIcon(R.drawable.icon)
|
||||
.setMultiChoiceItems(items, checkedItems, multiChoiceClickListener)
|
||||
.setPositiveButton(android.R.string.ok, clickListener)
|
||||
.setNegativeButton(android.R.string.cancel, clickListener)
|
||||
.create();
|
||||
|
||||
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
Logger.debug(LOG_TAG, "onCancel: unchecking.");
|
||||
chooseCheckBox.setChecked(false);
|
||||
}
|
||||
});
|
||||
|
||||
chooseCheckBox.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// There appears to be no way to stop Android interpreting the click
|
||||
// first. So, if the user clicked on an unchecked box, it's checked by
|
||||
// the time we get here.
|
||||
if (!chooseCheckBox.isChecked()) {
|
||||
Logger.debug(LOG_TAG, "onClick: was checked, not showing dialog.");
|
||||
return;
|
||||
}
|
||||
Logger.debug(LOG_TAG, "onClick: was unchecked, showing dialog.");
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.mozilla.gecko.sync.Utils;
|
|||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
|
@ -239,6 +240,10 @@ public class AndroidFxAccount {
|
|||
return Utils.getPrefsPath(product, username, serverURLThing, profile, version);
|
||||
}
|
||||
|
||||
public SharedPreferences getSyncPrefs() throws UnsupportedEncodingException, GeneralSecurityException {
|
||||
return context.getSharedPreferences(getSyncPrefsPath(), Utils.SHARED_PREFERENCES_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a JSON dictionary of the string values associated to this account.
|
||||
* <p>
|
||||
|
@ -315,7 +320,7 @@ public class AndroidFxAccount {
|
|||
}
|
||||
|
||||
public void clearSyncPrefs() throws UnsupportedEncodingException, GeneralSecurityException {
|
||||
context.getSharedPreferences(getSyncPrefsPath(), Utils.SHARED_PREFERENCES_MODE).edit().clear().commit();
|
||||
getSyncPrefs().edit().clear().commit();
|
||||
}
|
||||
|
||||
public void enableSyncing() {
|
||||
|
|
|
@ -35,7 +35,6 @@ import org.mozilla.gecko.sync.ExtendedJSONObject;
|
|||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
|
||||
import org.mozilla.gecko.sync.SyncConfiguration;
|
||||
import org.mozilla.gecko.sync.Utils;
|
||||
import org.mozilla.gecko.sync.crypto.KeyBundle;
|
||||
import org.mozilla.gecko.sync.delegates.BaseGlobalSessionCallback;
|
||||
import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
|
||||
|
@ -219,7 +218,6 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||
protected void syncWithAssertion(final String audience,
|
||||
final String assertion,
|
||||
URI tokenServerEndpointURI,
|
||||
final String prefsPath,
|
||||
final SharedPreferences sharedPrefs,
|
||||
final KeyBundle syncKeyBundle,
|
||||
final String clientState,
|
||||
|
@ -332,10 +330,8 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||
return;
|
||||
}
|
||||
|
||||
final String prefsPath = fxAccount.getSyncPrefsPath();
|
||||
|
||||
// This will be the same chunk of SharedPreferences that GlobalSession/SyncConfiguration will later create.
|
||||
final SharedPreferences sharedPrefs = context.getSharedPreferences(prefsPath, Utils.SHARED_PREFERENCES_MODE);
|
||||
final SharedPreferences sharedPrefs = fxAccount.getSyncPrefs();
|
||||
|
||||
final String audience = fxAccount.getAudience();
|
||||
final String authServerEndpoint = fxAccount.getAccountServerURI();
|
||||
|
@ -389,7 +385,7 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||
now + skewHandler.getSkewInMillis(),
|
||||
this.getAssertionDurationInMilliseconds());
|
||||
final BaseGlobalSessionCallback sessionCallback = new SessionCallback(syncDelegate);
|
||||
syncWithAssertion(audience, assertion, tokenServerEndpointURI, prefsPath, sharedPrefs, married.getSyncKeyBundle(), married.getClientState(), sessionCallback);
|
||||
syncWithAssertion(audience, assertion, tokenServerEndpointURI, sharedPrefs, married.getSyncKeyBundle(), married.getClientState(), sessionCallback);
|
||||
} catch (Exception e) {
|
||||
syncDelegate.handleError(e);
|
||||
return;
|
||||
|
|
|
@ -917,7 +917,29 @@ public class GlobalSession implements PrefsSource, HttpResponseObserver {
|
|||
return config.enabledEngineNames;
|
||||
}
|
||||
|
||||
return SyncConfiguration.validEngineNames();
|
||||
// These are the default set of engine names.
|
||||
Set<String> validEngineNames = SyncConfiguration.validEngineNames();
|
||||
|
||||
// If the user hasn't set any selected engines, that's okay -- default to
|
||||
// everything.
|
||||
if (config.userSelectedEngines == null) {
|
||||
return validEngineNames;
|
||||
}
|
||||
|
||||
// userSelectedEngines has keys that are engine names, and boolean values
|
||||
// corresponding to whether the user asked for the engine to sync or not. If
|
||||
// an engine is not present, that means the user didn't change its sync
|
||||
// setting. Since we default to everything on, that means the user didn't
|
||||
// turn it off; therefore, it's included in the set of engines to sync.
|
||||
Set<String> validAndSelectedEngineNames = new HashSet<String>();
|
||||
for (String engineName : validEngineNames) {
|
||||
if (config.userSelectedEngines.containsKey(engineName) &&
|
||||
!config.userSelectedEngines.get(engineName)) {
|
||||
continue;
|
||||
}
|
||||
validAndSelectedEngineNames.add(engineName);
|
||||
}
|
||||
return validAndSelectedEngineNames;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -112,7 +112,7 @@ public class TokenServerClient {
|
|||
// Responses should *always* be JSON, even in the case of 4xx and 5xx
|
||||
// errors. If we don't see JSON, the server is likely very unhappy.
|
||||
String contentType = response.getEntity().getContentType().getValue();
|
||||
if (contentType != "application/json" && !contentType.startsWith("application/json;")) {
|
||||
if (!contentType.equals("application/json") && !contentType.startsWith("application/json;")) {
|
||||
Logger.warn(LOG_TAG, "Got non-JSON response with Content-Type " +
|
||||
contentType + ". Misconfigured server?");
|
||||
throw new TokenServerMalformedResponseException(null, "Non-JSON response Content-Type.");
|
||||
|
|
Загрузка…
Ссылка в новой задаче