Bug 1098667 - Part 1: Import from android-sync. r=rnewman

--HG--
extra : rebase_source : 4ebbd8323bcf7d96f91f6df114d1a61703e3b8bc
This commit is contained in:
Nick Alexander 2014-11-25 16:18:53 -08:00
parent b8e5cfdcea
commit 2d5fa962ef
19 changed files with 576 additions and 163 deletions

View File

@ -838,10 +838,13 @@ sync_java_files = [
'fxa/AccountLoader.java',
'fxa/activities/FxAccountAbstractActivity.java',
'fxa/activities/FxAccountAbstractSetupActivity.java',
'fxa/activities/FxAccountAbstractUpdateCredentialsActivity.java',
'fxa/activities/FxAccountConfirmAccountActivity.java',
'fxa/activities/FxAccountCreateAccountActivity.java',
'fxa/activities/FxAccountCreateAccountNotAllowedActivity.java',
'fxa/activities/FxAccountFinishMigratingActivity.java',
'fxa/activities/FxAccountGetStartedActivity.java',
'fxa/activities/FxAccountMigrationFinishedActivity.java',
'fxa/activities/FxAccountSignInActivity.java',
'fxa/activities/FxAccountStatusActivity.java',
'fxa/activities/FxAccountStatusFragment.java',
@ -861,6 +864,7 @@ sync_java_files = [
'fxa/login/FxAccountLoginStateMachine.java',
'fxa/login/FxAccountLoginTransition.java',
'fxa/login/Married.java',
'fxa/login/MigratedFromSync11.java',
'fxa/login/Separated.java',
'fxa/login/State.java',
'fxa/login/StateFactory.java',

View File

@ -0,0 +1,181 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.fxa.activities;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountClient;
import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
import org.mozilla.gecko.background.fxa.FxAccountClient20;
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.fxa.FirefoxAccounts;
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.fxa.tasks.FxAccountSignInTask;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
* Abstract activity which displays a screen for updating the local password.
*/
public abstract class FxAccountAbstractUpdateCredentialsActivity extends FxAccountAbstractSetupActivity {
protected static final String LOG_TAG = FxAccountAbstractUpdateCredentialsActivity.class.getSimpleName();
protected AndroidFxAccount fxAccount;
protected final int layoutResourceId;
public FxAccountAbstractUpdateCredentialsActivity(int layoutResourceId) {
// We want to share code with the other setup activities, but this activity
// doesn't create a new Android Account, it modifies an existing one. If you
// manage to get an account, and somehow be locked out too, we'll let you
// update it.
super(CANNOT_RESUME_WHEN_NO_ACCOUNTS_EXIST);
this.layoutResourceId = layoutResourceId;
}
/**
* {@inheritDoc}
*/
@Override
public void onCreate(Bundle icicle) {
Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
super.onCreate(icicle);
setContentView(layoutResourceId);
emailEdit = (AutoCompleteTextView) ensureFindViewById(null, R.id.email, "email edit");
passwordEdit = (EditText) ensureFindViewById(null, R.id.password, "password edit");
showPasswordButton = (Button) ensureFindViewById(null, R.id.show_password, "show password button");
remoteErrorTextView = (TextView) ensureFindViewById(null, R.id.remote_error, "remote error text view");
button = (Button) ensureFindViewById(null, R.id.button, "update credentials");
progressBar = (ProgressBar) ensureFindViewById(null, R.id.progress, "progress bar");
minimumPasswordLength = 1; // Minimal restriction on passwords entered to sign in.
createButton();
addListeners();
updateButtonState();
createShowPasswordButton();
emailEdit.setEnabled(false);
TextView view = (TextView) findViewById(R.id.forgot_password_link);
ActivityUtils.linkTextView(view, R.string.fxaccount_sign_in_forgot_password, R.string.fxaccount_link_forgot_password);
updateFromIntentExtras();
}
protected class UpdateCredentialsDelegate implements RequestDelegate<LoginResponse> {
public final String email;
public final String serverURI;
public final PasswordStretcher passwordStretcher;
public UpdateCredentialsDelegate(String email, PasswordStretcher passwordStretcher, String serverURI) {
this.email = email;
this.serverURI = serverURI;
this.passwordStretcher = passwordStretcher;
}
@Override
public void handleError(Exception e) {
showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error);
}
@Override
public void handleFailure(FxAccountClientRemoteException e) {
if (e.isUpgradeRequired()) {
Logger.error(LOG_TAG, "Got upgrade required from remote server; transitioning Firefox Account to Doghouse state.");
final State state = fxAccount.getState();
fxAccount.setState(state.makeDoghouseState());
// The status activity will say that the user needs to upgrade.
redirectToActivity(FxAccountStatusActivity.class);
return;
}
showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error);
}
@Override
public void handleSuccess(LoginResponse result) {
Logger.info(LOG_TAG, "Got success signing in.");
if (fxAccount == null) {
this.handleError(new IllegalStateException("fxAccount must not be null"));
return;
}
byte[] unwrapkB;
try {
// It is crucial that we use the email address provided by the server
// (rather than whatever the user entered), because the user's keys are
// wrapped and salted with the initial email they provided to
// /create/account. Of course, we want to pass through what the user
// entered locally as much as possible.
byte[] quickStretchedPW = passwordStretcher.getQuickStretchedPW(result.remoteEmail.getBytes("UTF-8"));
unwrapkB = FxAccountUtils.generateUnwrapBKey(quickStretchedPW);
} catch (Exception e) {
this.handleError(e);
return;
}
fxAccount.setState(new Engaged(email, result.uid, result.verified, unwrapkB, result.sessionToken, result.keyFetchToken));
fxAccount.requestSync(FirefoxAccounts.FORCE);
// For great debugging.
if (FxAccountUtils.LOG_PERSONAL_INFORMATION) {
fxAccount.dump();
}
setResult(RESULT_OK);
// Maybe show success activity.
final Intent successIntent = makeSuccessIntent(email, result);
if (successIntent != null) {
startActivity(successIntent);
}
finish();
}
}
public void updateCredentials(String email, String password) {
String serverURI = fxAccount.getAccountServerURI();
Executor executor = Executors.newSingleThreadExecutor();
FxAccountClient client = new FxAccountClient20(serverURI, executor);
PasswordStretcher passwordStretcher = makePasswordStretcher(password);
try {
hideRemoteError();
RequestDelegate<LoginResponse> delegate = new UpdateCredentialsDelegate(email, passwordStretcher, serverURI);
new FxAccountSignInTask(this, this, email, passwordStretcher, client, delegate).execute();
} catch (Exception e) {
Logger.warn(LOG_TAG, "Got exception updating credentials for account.", e);
showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error);
}
}
protected void createButton() {
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final String email = emailEdit.getText().toString();
final String password = passwordEdit.getText().toString();
updateCredentials(email, password);
}
});
}
}

View File

@ -7,7 +7,6 @@ package org.mozilla.gecko.fxa.activities;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.Engaged;
import org.mozilla.gecko.fxa.login.State;
@ -141,9 +140,6 @@ public class FxAccountConfirmAccountActivity extends FxAccountAbstractActivity i
case NeedsVerification:
// This is what we're here to handle.
break;
case NeedsPassword:
case NeedsUpgrade:
case None:
default:
// We're not in the right place! Redirect to status.
Logger.warn(LOG_TAG, "No need to verify Firefox Account that needs action " + neededAction.toString() +

View File

@ -0,0 +1,54 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.fxa.activities;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.login.State.StateLabel;
import android.content.Intent;
/**
* Activity which displays a screen for inputting the password and finishing
* migrating to Firefox Accounts / Sync 1.5.
*/
public class FxAccountFinishMigratingActivity extends FxAccountAbstractUpdateCredentialsActivity {
protected static final String LOG_TAG = FxAccountFinishMigratingActivity.class.getSimpleName();
public FxAccountFinishMigratingActivity() {
super(R.layout.fxaccount_finish_migrating);
}
@Override
public void onResume() {
super.onResume();
this.fxAccount = getAndroidFxAccount();
if (fxAccount == null) {
Logger.warn(LOG_TAG, "Could not get Firefox Account.");
setResult(RESULT_CANCELED);
finish();
return;
}
final State state = fxAccount.getState();
if (state.getStateLabel() != StateLabel.MigratedFromSync11) {
Logger.warn(LOG_TAG, "Cannot finish migrating from Firefox Account in state: " + state.getStateLabel());
setResult(RESULT_CANCELED);
finish();
return;
}
emailEdit.setText(fxAccount.getEmail());
}
@Override
public Intent makeSuccessIntent(String email, LoginResponse result) {
final Intent successIntent = new Intent(this, FxAccountMigrationFinishedActivity.class);
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
// the soft keyboard not being shown for the started activity. Why, Android, why?
successIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
return successIntent;
}
}

View File

@ -0,0 +1,67 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.fxa.activities;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.login.State.Action;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
/**
* Activity which displays "Upgrade finished" success screen.
*/
public class FxAccountMigrationFinishedActivity extends FxAccountAbstractActivity {
private static final String LOG_TAG = FxAccountMigrationFinishedActivity.class.getSimpleName();
protected AndroidFxAccount fxAccount;
public FxAccountMigrationFinishedActivity() {
super(CANNOT_RESUME_WHEN_NO_ACCOUNTS_EXIST);
}
/**
* {@inheritDoc}
*/
@Override
public void onCreate(Bundle icicle) {
Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
super.onCreate(icicle);
setContentView(R.layout.fxaccount_migration_finished);
}
@Override
public void onResume() {
super.onResume();
this.fxAccount = getAndroidFxAccount();
if (fxAccount == null) {
Logger.warn(LOG_TAG, "Could not get Firefox Account.");
setResult(RESULT_CANCELED);
finish();
return;
}
final State state = fxAccount.getState();
if (state.getNeededAction() == Action.NeedsFinishMigrating) {
Logger.warn(LOG_TAG, "Firefox Account needs to finish migrating; not displaying migration finished activity.");
setResult(RESULT_CANCELED);
finish();
return;
}
final View backToBrowsingButton = ensureFindViewById(null, R.id.button, "back to browsing button");
backToBrowsingButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ActivityUtils.openURLInFennec(v.getContext(), null);
}
});
}
}

View File

@ -5,13 +5,12 @@
package org.mozilla.gecko.fxa.activities;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.LocaleAware.LocaleAwareFragmentActivity;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.LocaleAware.LocaleAwareActivity;
import org.mozilla.gecko.LocaleAware.LocaleAwareFragmentActivity;
import android.accounts.Account;
import android.accounts.AccountManager;

View File

@ -77,6 +77,7 @@ public class FxAccountStatusFragment
protected Preference needsVerificationPreference;
protected Preference needsMasterSyncAutomaticallyEnabledPreference;
protected Preference needsAccountEnabledPreference;
protected Preference needsFinishMigratingPreference;
protected PreferenceCategory syncCategory;
@ -138,6 +139,7 @@ public class FxAccountStatusFragment
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");
@ -157,6 +159,7 @@ public class FxAccountStatusFragment
needsPasswordPreference.setOnPreferenceClickListener(this);
needsVerificationPreference.setOnPreferenceClickListener(this);
needsAccountEnabledPreference.setOnPreferenceClickListener(this);
needsFinishMigratingPreference.setOnPreferenceClickListener(this);
bookmarksPreference.setOnPreferenceClickListener(this);
historyPreference.setOnPreferenceClickListener(this);
@ -204,6 +207,20 @@ public class FxAccountStatusFragment
return true;
}
if (preference == needsFinishMigratingPreference) {
final Intent intent = new Intent(getActivity(), FxAccountFinishMigratingActivity.class);
final Bundle extras = getExtrasForAccount();
if (extras != null) {
intent.putExtras(extras);
}
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
// the soft keyboard not being shown for the started activity. Why, Android, why?
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(intent);
return true;
}
if (preference == needsVerificationPreference) {
FxAccountCodeResender.resendCode(getActivity().getApplicationContext(), fxAccount);
@ -280,6 +297,7 @@ public class FxAccountStatusFragment
this.needsVerificationPreference,
this.needsMasterSyncAutomaticallyEnabledPreference,
this.needsAccountEnabledPreference,
this.needsFinishMigratingPreference,
};
for (Preference errorPreference : errorPreferences) {
final boolean currentlyShown = null != findPreference(errorPreference.getKey());
@ -325,6 +343,12 @@ public class FxAccountStatusFragment
setCheckboxesEnabled(false);
}
protected void showNeedsFinishMigrating() {
syncCategory.setTitle(R.string.fxaccount_status_sync);
showOnlyOneErrorPreference(needsFinishMigratingPreference);
setCheckboxesEnabled(false);
}
protected void showConnected() {
syncCategory.setTitle(R.string.fxaccount_status_sync_enabled);
showOnlyOneErrorPreference(null);
@ -464,8 +488,12 @@ public class FxAccountStatusFragment
case NeedsVerification:
showNeedsVerification();
break;
default:
case NeedsFinishMigrating:
showNeedsFinishMigrating();
break;
case None:
showConnected();
break;
}
// We check for the master setting last, since it is not strictly
@ -703,6 +731,11 @@ public class FxAccountStatusFragment
State state = fxAccount.getState();
fxAccount.setState(state.makeDoghouseState());
refresh();
} else if ("debug_migrated_from_sync11".equals(key)) {
Logger.info(LOG_TAG, "Moving to MigratedFromSync11 state: Requiring password.");
State state = fxAccount.getState();
fxAccount.setState(state.makeMigratedFromSync11State(null));
refresh();
} else {
return false;
}
@ -729,7 +762,8 @@ public class FxAccountStatusFragment
"debug_force_sync",
"debug_forget_certificate",
"debug_require_password",
"debug_require_upgrade" };
"debug_require_upgrade",
"debug_migrated_from_sync11" };
for (String debugKey : debugKeys) {
final Preference button = ensureFindPreference(debugKey);
button.setTitle(debugKey); // Not very friendly, but this is for debugging only!

View File

@ -4,80 +4,22 @@
package org.mozilla.gecko.fxa.activities;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountClient;
import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
import org.mozilla.gecko.background.fxa.FxAccountClient20;
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.fxa.FirefoxAccounts;
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.fxa.login.State.StateLabel;
import org.mozilla.gecko.fxa.tasks.FxAccountSignInTask;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.content.Intent;
/**
* Activity which displays a screen for updating the local password.
*/
public class FxAccountUpdateCredentialsActivity extends FxAccountAbstractSetupActivity {
public class FxAccountUpdateCredentialsActivity extends FxAccountAbstractUpdateCredentialsActivity {
protected static final String LOG_TAG = FxAccountUpdateCredentialsActivity.class.getSimpleName();
protected AndroidFxAccount fxAccount;
public FxAccountUpdateCredentialsActivity() {
// We want to share code with the other setup activities, but this activity
// doesn't create a new Android Account, it modifies an existing one. If you
// manage to get an account, and somehow be locked out too, we'll let you
// update it.
super(CANNOT_RESUME_WHEN_NO_ACCOUNTS_EXIST);
}
/**
* {@inheritDoc}
*/
@Override
public void onCreate(Bundle icicle) {
Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
super.onCreate(icicle);
setContentView(R.layout.fxaccount_update_credentials);
emailEdit = (AutoCompleteTextView) ensureFindViewById(null, R.id.email, "email edit");
passwordEdit = (EditText) ensureFindViewById(null, R.id.password, "password edit");
showPasswordButton = (Button) ensureFindViewById(null, R.id.show_password, "show password button");
remoteErrorTextView = (TextView) ensureFindViewById(null, R.id.remote_error, "remote error text view");
button = (Button) ensureFindViewById(null, R.id.button, "update credentials");
progressBar = (ProgressBar) ensureFindViewById(null, R.id.progress, "progress bar");
minimumPasswordLength = 1; // Minimal restriction on passwords entered to sign in.
createButton();
addListeners();
updateButtonState();
createShowPasswordButton();
emailEdit.setEnabled(false);
TextView view = (TextView) findViewById(R.id.forgot_password_link);
ActivityUtils.linkTextView(view, R.string.fxaccount_sign_in_forgot_password, R.string.fxaccount_link_forgot_password);
updateFromIntentExtras();
super(R.layout.fxaccount_update_credentials);
}
@Override
@ -90,7 +32,7 @@ public class FxAccountUpdateCredentialsActivity extends FxAccountAbstractSetupAc
finish();
return;
}
State state = fxAccount.getState();
final State state = fxAccount.getState();
if (state.getStateLabel() != StateLabel.Separated) {
Logger.warn(LOG_TAG, "Cannot update credentials from Firefox Account in state: " + state.getStateLabel());
setResult(RESULT_CANCELED);
@ -100,93 +42,11 @@ public class FxAccountUpdateCredentialsActivity extends FxAccountAbstractSetupAc
emailEdit.setText(fxAccount.getEmail());
}
protected class UpdateCredentialsDelegate implements RequestDelegate<LoginResponse> {
public final String email;
public final String serverURI;
public final PasswordStretcher passwordStretcher;
public UpdateCredentialsDelegate(String email, PasswordStretcher passwordStretcher, String serverURI) {
this.email = email;
this.serverURI = serverURI;
this.passwordStretcher = passwordStretcher;
}
@Override
public void handleError(Exception e) {
showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error);
}
@Override
public void handleFailure(FxAccountClientRemoteException e) {
if (e.isUpgradeRequired()) {
Logger.error(LOG_TAG, "Got upgrade required from remote server; transitioning Firefox Account to Doghouse state.");
final State state = fxAccount.getState();
fxAccount.setState(state.makeDoghouseState());
// The status activity will say that the user needs to upgrade.
redirectToActivity(FxAccountStatusActivity.class);
return;
}
showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error);
}
@Override
public void handleSuccess(LoginResponse result) {
Logger.info(LOG_TAG, "Got success signing in.");
if (fxAccount == null) {
this.handleError(new IllegalStateException("fxAccount must not be null"));
return;
}
byte[] unwrapkB;
try {
// It is crucial that we use the email address provided by the server
// (rather than whatever the user entered), because the user's keys are
// wrapped and salted with the initial email they provided to
// /create/account. Of course, we want to pass through what the user
// entered locally as much as possible.
byte[] quickStretchedPW = passwordStretcher.getQuickStretchedPW(result.remoteEmail.getBytes("UTF-8"));
unwrapkB = FxAccountUtils.generateUnwrapBKey(quickStretchedPW);
} catch (Exception e) {
this.handleError(e);
return;
}
fxAccount.setState(new Engaged(email, result.uid, result.verified, unwrapkB, result.sessionToken, result.keyFetchToken));
fxAccount.requestSync(FirefoxAccounts.FORCE);
// For great debugging.
if (FxAccountUtils.LOG_PERSONAL_INFORMATION) {
fxAccount.dump();
}
setResult(RESULT_OK);
finish();
}
}
public void updateCredentials(String email, String password) {
String serverURI = fxAccount.getAccountServerURI();
Executor executor = Executors.newSingleThreadExecutor();
FxAccountClient client = new FxAccountClient20(serverURI, executor);
PasswordStretcher passwordStretcher = makePasswordStretcher(password);
try {
hideRemoteError();
RequestDelegate<LoginResponse> delegate = new UpdateCredentialsDelegate(email, passwordStretcher, serverURI);
new FxAccountSignInTask(this, this, email, passwordStretcher, client, delegate).execute();
} catch (Exception e) {
Logger.warn(LOG_TAG, "Got exception updating credentials for account.", e);
showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error);
}
}
protected void createButton() {
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final String email = emailEdit.getText().toString();
final String password = passwordEdit.getText().toString();
updateCredentials(email, password);
}
});
@Override
public Intent makeSuccessIntent(String email, LoginResponse result) {
// We don't show anything after updating credentials. The updating Activity
// sets its result to OK and the user is returned to the previous task,
// which is often the Status Activity.
return null;
}
}

View File

@ -0,0 +1,28 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.fxa.login;
import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine.ExecuteDelegate;
import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.PasswordRequired;
public class MigratedFromSync11 extends State {
public final String password;
public MigratedFromSync11(String email, String uid, boolean verified, String password) {
super(StateLabel.MigratedFromSync11, email, uid, verified);
// Null password is allowed.
this.password = password;
}
@Override
public void execute(final ExecuteDelegate delegate) {
delegate.handleTransition(new PasswordRequired(), this);
}
@Override
public Action getNeededAction() {
return Action.NeedsFinishMigrating;
}
}

View File

@ -9,7 +9,7 @@ import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.Utils;
public abstract class State {
public static final long CURRENT_VERSION = 2L;
public static final long CURRENT_VERSION = 3L;
public enum StateLabel {
Engaged,
@ -17,12 +17,14 @@ public abstract class State {
Married,
Separated,
Doghouse,
MigratedFromSync11,
}
public enum Action {
NeedsUpgrade,
NeedsPassword,
NeedsVerification,
NeedsFinishMigrating,
None,
}
@ -60,6 +62,10 @@ public abstract class State {
return new Doghouse(email, uid, verified);
}
public State makeMigratedFromSync11State(String password) {
return new MigratedFromSync11(email, uid, verified, password);
}
public abstract void execute(ExecuteDelegate delegate);
public abstract Action getNeededAction();

View File

@ -54,8 +54,11 @@ public class StateFactory {
}
final int v = version.intValue();
if (v == 2) {
if (v == 3) {
// The most common case is the most recent version.
return fromJSONObjectV3(stateLabel, o);
}
if (v == 2) {
return fromJSONObjectV2(stateLabel, o);
}
if (v == 1) {
@ -134,6 +137,23 @@ public class StateFactory {
}
}
/**
* Exactly the same as {@link fromJSONObjectV2}, except that there's a new
* MigratedFromSyncV11 state.
*/
protected static State fromJSONObjectV3(StateLabel stateLabel, ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException, NonObjectJSONException {
switch (stateLabel) {
case MigratedFromSync11:
return new MigratedFromSync11(
o.getString("email"),
o.getString("uid"),
o.getBoolean("verified"),
o.getString("password"));
default:
return fromJSONObjectV2(stateLabel, o);
}
}
protected static void logMigration(State from, State to) {
if (!FxAccountUtils.LOG_PERSONAL_INFORMATION) {
return;

View File

@ -8,6 +8,7 @@ import org.mozilla.gecko.BrowserLocaleManager;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountUtils;
import org.mozilla.gecko.fxa.activities.FxAccountFinishMigratingActivity;
import org.mozilla.gecko.fxa.activities.FxAccountStatusActivity;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.State;
@ -67,12 +68,21 @@ public class FxAccountNotificationManager {
BrowserLocaleManager.getInstance().getAndApplyPersistedLocale(context);
}
final String title = context.getResources().getString(R.string.fxaccount_sync_sign_in_error_notification_title);
final String text = context.getResources().getString(R.string.fxaccount_sync_sign_in_error_notification_text, state.email);
final String title;
final String text;
final Intent notificationIntent;
if (action == Action.NeedsFinishMigrating) {
title = context.getResources().getString(R.string.fxaccount_sync_finish_migrating_notification_title);
text = context.getResources().getString(R.string.fxaccount_sync_finish_migrating_notification_text, state.email);
notificationIntent = new Intent(context, FxAccountFinishMigratingActivity.class);
} else {
title = context.getResources().getString(R.string.fxaccount_sync_sign_in_error_notification_title);
text = context.getResources().getString(R.string.fxaccount_sync_sign_in_error_notification_text, state.email);
notificationIntent = new Intent(context, FxAccountStatusActivity.class);
}
Logger.info(LOG_TAG, "State " + state.getStateLabel() + " needs action; offering notification with title: " + title);
FxAccountUtils.pii(LOG_TAG, "And text: " + text);
final Intent notificationIntent = new Intent(context, FxAccountStatusActivity.class);
final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
final Builder builder = new NotificationCompat.Builder(context);

View File

@ -118,6 +118,7 @@ public class FxAccountSchedulePolicy implements SchedulePolicy {
switch (needed) {
case NeedsPassword:
case NeedsUpgrade:
case NeedsFinishMigrating:
requestPeriodicSync(POLL_INTERVAL_ERROR_STATE_SEC);
break;
case NeedsVerification:

View File

@ -180,10 +180,16 @@
<!ENTITY fxaccount_account_verified_sub_header 'Account verified'>
<!ENTITY fxaccount_account_verified_description2 'Your data will begin syncing momentarily.'>
<!ENTITY fxaccount_migration_finished_header 'Upgrade finished'>
<!ENTITY fxaccount_update_credentials_header 'Sign in'>
<!ENTITY fxaccount_update_credentials_button_label 'Sign in'>
<!ENTITY fxaccount_update_credentials_unknown_error 'Could not sign in'>
<!ENTITY fxaccount_finish_migrating_header 'Sign in to finish upgrading'>
<!ENTITY fxaccount_finish_migrating_button_label 'Finish upgrading'>
<!ENTITY fxaccount_finish_migrating_description 'Upgrading can transfer a lot of data. It\&apos;s best to be on a WiFi network.'>
<!ENTITY fxaccount_status_header2 'Firefox Account'>
<!ENTITY fxaccount_status_signed_in_as 'Signed in as'>
<!ENTITY fxaccount_status_auth_server 'Account server'>
@ -198,6 +204,7 @@
<!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_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'>
<!ENTITY fxaccount_status_passwords 'Passwords'>
@ -250,3 +257,8 @@
<!-- Localization note: the format string below will be replaced
with the Firefox Account's email address. -->
<!ENTITY fxaccount_sync_sign_in_error_notification_text2 'Tap to sign in as &formatS;'>
<!ENTITY fxaccount_sync_finish_migrating_notification_title 'Finish upgrading &syncBrand.shortName.label;?'>
<!-- Localization note: the format string below will be replaced
with the Firefox Account's email address. -->
<!ENTITY fxaccount_sync_finish_migrating_notification_text 'Tap to sign in as &formatS;'>

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true" >
<LinearLayout
android:id="@+id/update_credentials_view"
style="@style/FxAccountMiddle" >
<LinearLayout style="@style/FxAccountSpacer" />
<TextView
style="@style/FxAccountHeaderItem"
android:text="@string/fxaccount_finish_migrating_header" />
<include layout="@layout/fxaccount_custom_server_view" />
<include layout="@layout/fxaccount_email_password_view" />
<TextView
style="@style/FxAccountTextItem"
android:layout_marginTop="10dp"
android:text="@string/fxaccount_finish_migrating_description" />
<TextView
android:id="@+id/remote_error"
style="@style/FxAccountErrorItem" />
<RelativeLayout style="@style/FxAccountButtonLayout" >
<ProgressBar
android:id="@+id/progress"
style="@style/FxAccountProgress" />
<Button
android:id="@+id/button"
style="@style/FxAccountButton"
android:text="@string/fxaccount_finish_migrating_button_label" />
</RelativeLayout>
<TextView
android:id="@+id/forgot_password_link"
style="@style/FxAccountLinkifiedItem"
android:layout_marginTop="10dp"
android:text="@string/fxaccount_sign_in_forgot_password" />
<LinearLayout style="@style/FxAccountSpacer" />
<ImageView
style="@style/FxAccountIcon"
android:contentDescription="@string/fxaccount_empty_contentDescription" />
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true" >
<LinearLayout style="@style/FxAccountMiddle" >
<TextView
style="@style/FxAccountHeaderItem"
android:text="@string/fxaccount_migration_finished_header" >
</TextView>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="45dp"
android:contentDescription="@string/fxaccount_empty_contentDescription"
android:src="@drawable/fxaccount_checkbox" >
</ImageView>
<TextView
style="@style/FxAccountTextItem"
android:layout_marginBottom="40dp"
android:text="@string/fxaccount_migration_finished_description"
android:textSize="18sp" />
<Button
android:id="@+id/button"
style="@style/FxAccountButton"
android:text="@string/fxaccount_back_to_browsing" />
<LinearLayout style="@style/FxAccountSpacer" />
<ImageView
style="@style/FxAccountIcon"
android:contentDescription="@string/fxaccount_empty_contentDescription" />
</LinearLayout>
</ScrollView>

View File

@ -54,6 +54,13 @@
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"
android:key="needs_finish_migrating"
android:layout="@layout/fxaccount_status_error_preference"
android:persistent="false"
android:title="@string/fxaccount_status_needs_finish_migrating" />
<Preference
android:editable="false"
@ -125,6 +132,7 @@
<Preference android:key="debug_forget_certificate" />
<Preference android:key="debug_require_password" />
<Preference android:key="debug_require_upgrade" />
<Preference android:key="debug_migrated_from_sync11" />
</PreferenceCategory>
</PreferenceScreen>

View File

@ -67,6 +67,21 @@
android:windowSoftInputMode="adjustResize">
</activity>
<activity
android:theme="@style/FxAccountTheme"
android:name="org.mozilla.gecko.fxa.activities.FxAccountFinishMigratingActivity"
android:configChanges="locale|layoutDirection"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
android:theme="@style/FxAccountTheme"
android:name="org.mozilla.gecko.fxa.activities.FxAccountMigrationFinishedActivity"
android:configChanges="locale|layoutDirection"
android:noHistory="true"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
android:theme="@style/FxAccountTheme"
android:name="org.mozilla.gecko.fxa.activities.FxAccountCreateAccountNotAllowedActivity"

View File

@ -167,10 +167,17 @@
<string name="fxaccount_account_verified_sub_header">&fxaccount_account_verified_sub_header;</string>
<string name="fxaccount_account_verified_description">&fxaccount_account_verified_description2;</string>
<string name="fxaccount_migration_finished_header">&fxaccount_migration_finished_header;</string>
<string name="fxaccount_migration_finished_description">&fxaccount_account_verified_description2;</string>
<string name="fxaccount_update_credentials_header">&fxaccount_update_credentials_header;</string>
<string name="fxaccount_update_credentials_button_label">&fxaccount_update_credentials_button_label;</string>
<string name="fxaccount_update_credentials_unknown_error">&fxaccount_update_credentials_unknown_error;</string>
<string name="fxaccount_finish_migrating_header">&fxaccount_finish_migrating_header;</string>
<string name="fxaccount_finish_migrating_button_label">&fxaccount_finish_migrating_button_label;</string>
<string name="fxaccount_finish_migrating_description">&fxaccount_finish_migrating_description;</string>
<string name="fxaccount_status_activity_label">&syncBrand.shortName.label;</string>
<string name="fxaccount_status_header">&fxaccount_status_header2;</string>
<string name="fxaccount_status_signed_in_as">&fxaccount_status_signed_in_as;</string>
@ -187,6 +194,7 @@
<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_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>
@ -219,3 +227,6 @@
<string name="fxaccount_remove_account_dialog_message">&fxaccount_remove_account_dialog_message;</string>
<string name="fxaccount_remove_account_toast">&fxaccount_remove_account_toast;</string>
<string name="fxaccount_remove_account_menu_item">&fxaccount_remove_account_menu_item;</string>
<string name="fxaccount_sync_finish_migrating_notification_title">&fxaccount_sync_finish_migrating_notification_title;</string>
<string name="fxaccount_sync_finish_migrating_notification_text">&fxaccount_sync_finish_migrating_notification_text;</string>