Bug 958889 - Part 2.7: Provide public read-only access to FXAccount state and resend verification code. r=nalexander

This commit is contained in:
Michael Comella 2014-05-22 18:38:02 -07:00
Родитель de6aa42072
Коммит 450e0dfa4f
12 изменённых файлов: 261 добавлений и 141 удалений

Просмотреть файл

@ -557,7 +557,6 @@ sync_java_files = [
'fxa/activities/FxAccountCreateAccountActivity.java',
'fxa/activities/FxAccountCreateAccountNotAllowedActivity.java',
'fxa/activities/FxAccountGetStartedActivity.java',
'fxa/activities/FxAccountSetupTask.java',
'fxa/activities/FxAccountSignInActivity.java',
'fxa/activities/FxAccountStatusActivity.java',
'fxa/activities/FxAccountStatusFragment.java',
@ -591,6 +590,10 @@ sync_java_files = [
'fxa/sync/FxAccountSyncService.java',
'fxa/sync/FxAccountSyncStatusHelper.java',
'fxa/sync/SchedulePolicy.java',
'fxa/tasks/FxAccountCodeResender.java',
'fxa/tasks/FxAccountCreateAccountTask.java',
'fxa/tasks/FxAccountSetupTask.java',
'fxa/tasks/FxAccountSignInTask.java',
'sync/AlreadySyncingException.java',
'sync/BackoffHandler.java',
'sync/BadRequiredFieldJSONException.java',

Просмотреть файл

@ -14,8 +14,10 @@ import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.fxa.authenticator.AccountPickler;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter;
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
import org.mozilla.gecko.sync.ThreadPool;
import org.mozilla.gecko.sync.Utils;
@ -156,6 +158,38 @@ public class FirefoxAccounts {
return null;
}
/**
* @return
* the {@link State} instance associated with the current account, or <code>null</code> if
* no accounts exist.
*/
public static State getFirefoxAccountState(final Context context) {
final Account account = getFirefoxAccount(context);
if (account == null) {
return null;
}
final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account);
try {
return fxAccount.getState();
} catch (final Exception ex) {
Logger.warn(LOG_TAG, "Could not get FX account state.", ex);
return null;
}
}
/*
* @param context Android context
* @return the email address associated with the configured Firefox account if one exists; null otherwise.
*/
public static String getFirefoxAccountEmail(final Context context) {
final Account account = getFirefoxAccount(context);
if (account == null) {
return null;
}
return account.name;
}
protected static void putHintsToSync(final Bundle extras, EnumSet<SyncHint> syncHints) {
// stagesToSync and stagesToSkip are allowed to be null.
if (syncHints == null) {
@ -275,4 +309,26 @@ public class FirefoxAccounts {
final String LOCALE = Utils.getLanguageTag(locale);
return res.getString(R.string.fxaccount_link_old_firefox, VERSION, OS, LOCALE);
}
/**
* Resends the account verification email, and displays an appropriate
* toast on both send success and failure. Note that because the underlying implementation
* uses {@link AsyncTask}, the provided context must be UI-capable, and this
* method called from the UI thread (see
* {@link org.mozilla.gecko.fxa.tasks.FxAccountCodeResender#resendCode(Context, AndroidFxAccount)}
* for more).
*
* @param context a UI-capable Android context.
* @return true if an account exists, false otherwise.
*/
public static boolean resendVerificationEmail(final Context context) {
final Account account = getFirefoxAccount(context);
if (account == null) {
return false;
}
final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account);
FxAccountCodeResender.resendCode(context, fxAccount);
return true;
}
}

Просмотреть файл

@ -19,10 +19,10 @@ import org.mozilla.gecko.background.fxa.FxAccountUtils;
import org.mozilla.gecko.background.fxa.PasswordStretcher;
import org.mozilla.gecko.background.fxa.QuickPasswordStretcher;
import org.mozilla.gecko.fxa.FxAccountConstants;
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.fxa.tasks.FxAccountSetupTask.ProgressDisplay;
import org.mozilla.gecko.sync.SyncConfiguration;
import org.mozilla.gecko.sync.setup.Constants;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;

Просмотреть файл

@ -4,21 +4,15 @@
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.FxAccountClientException.FxAccountClientRemoteException;
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.Action;
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
import android.accounts.Account;
@ -28,7 +22,6 @@ import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
import android.widget.Toast;
/**
* Activity which displays account created successfully screen to the user, and
@ -164,76 +157,8 @@ public class FxAccountConfirmAccountActivity extends FxAccountAbstractActivity i
resendLink.setClickable(resendLinkShouldBeEnabled);
}
public static class FxAccountResendCodeTask extends FxAccountSetupTask<Void> {
protected static final String LOG_TAG = FxAccountResendCodeTask.class.getSimpleName();
protected final byte[] sessionToken;
public FxAccountResendCodeTask(Context context, byte[] sessionToken, FxAccountClient client, RequestDelegate<Void> delegate) {
super(context, null, client, delegate);
this.sessionToken = sessionToken;
}
@Override
protected InnerRequestDelegate<Void> doInBackground(Void... arg0) {
try {
client.resendCode(sessionToken, innerDelegate);
latch.await();
return innerDelegate;
} catch (Exception e) {
Logger.error(LOG_TAG, "Got exception signing in.", e);
delegate.handleError(e);
}
return null;
}
}
protected static class ResendCodeDelegate implements RequestDelegate<Void> {
public final Context context;
public ResendCodeDelegate(Context context) {
this.context = context;
}
@Override
public void handleError(Exception e) {
Logger.warn(LOG_TAG, "Got exception requesting fresh confirmation link; ignoring.", e);
Toast.makeText(context, R.string.fxaccount_confirm_account_verification_link_not_sent, Toast.LENGTH_LONG).show();
}
@Override
public void handleFailure(FxAccountClientRemoteException e) {
handleError(e);
}
@Override
public void handleSuccess(Void result) {
Toast.makeText(context, R.string.fxaccount_confirm_account_verification_link_sent, Toast.LENGTH_SHORT).show();
}
}
public static void resendCode(Context context, AndroidFxAccount fxAccount) {
RequestDelegate<Void> delegate = new ResendCodeDelegate(context);
byte[] sessionToken;
try {
sessionToken = ((Engaged) fxAccount.getState()).getSessionToken();
} catch (Exception e) {
delegate.handleError(e);
return;
}
if (sessionToken == null) {
delegate.handleError(new IllegalStateException("sessionToken should not be null"));
return;
}
Executor executor = Executors.newSingleThreadExecutor();
FxAccountClient client = new FxAccountClient20(fxAccount.getAccountServerURI(), executor);
new FxAccountResendCodeTask(context, sessionToken, client, delegate).execute();
}
@Override
public void onClick(View v) {
resendCode(this, fxAccount);
FxAccountCodeResender.resendCode(this, fxAccount);
}
}

Просмотреть файл

@ -21,7 +21,7 @@ import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
import org.mozilla.gecko.background.fxa.PasswordStretcher;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.FxAccountCreateAccountTask;
import org.mozilla.gecko.fxa.tasks.FxAccountCreateAccountTask;
import android.app.AlertDialog;
import android.app.Dialog;

Просмотреть файл

@ -16,7 +16,7 @@ import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
import org.mozilla.gecko.background.fxa.PasswordStretcher;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.FxAccountSignInTask;
import org.mozilla.gecko.fxa.tasks.FxAccountSignInTask;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
import android.content.Intent;

Просмотреть файл

@ -17,6 +17,7 @@ import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.Married;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
import org.mozilla.gecko.sync.SyncConfiguration;
import android.accounts.Account;
@ -142,7 +143,7 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
}
if (preference == needsVerificationPreference) {
FxAccountConfirmAccountActivity.resendCode(getActivity().getApplicationContext(), fxAccount);
FxAccountCodeResender.resendCode(getActivity().getApplicationContext(), fxAccount);
Intent intent = new Intent(getActivity(), FxAccountConfirmAccountActivity.class);
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with

Просмотреть файл

@ -18,11 +18,11 @@ 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.FxAccountConstants;
import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.FxAccountSignInTask;
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;

Просмотреть файл

@ -0,0 +1,108 @@
/* 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.tasks;
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.FxAccountClientException.FxAccountClientRemoteException;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.Engaged;
import android.content.Context;
import android.widget.Toast;
/**
* A helper class that provides a simple interface for requesting
* a Firefox Account verification email to be resent.
*/
public class FxAccountCodeResender {
private static final String LOG_TAG = FxAccountCodeResender.class.getSimpleName();
private static class FxAccountResendCodeTask extends FxAccountSetupTask<Void> {
protected static final String LOG_TAG = FxAccountResendCodeTask.class.getSimpleName();
protected final byte[] sessionToken;
public FxAccountResendCodeTask(Context context, byte[] sessionToken, FxAccountClient client, RequestDelegate<Void> delegate) {
super(context, null, client, delegate);
this.sessionToken = sessionToken;
}
@Override
protected InnerRequestDelegate<Void> doInBackground(Void... arg0) {
try {
client.resendCode(sessionToken, innerDelegate);
latch.await();
return innerDelegate;
} catch (Exception e) {
Logger.error(LOG_TAG, "Got exception signing in.", e);
delegate.handleError(e);
}
return null;
}
}
private static class ResendCodeDelegate implements RequestDelegate<Void> {
public final Context context;
public ResendCodeDelegate(Context context) {
this.context = context;
}
@Override
public void handleError(Exception e) {
Logger.warn(LOG_TAG, "Got exception requesting fresh confirmation link; ignoring.", e);
Toast.makeText(context, R.string.fxaccount_confirm_account_verification_link_not_sent, Toast.LENGTH_LONG).show();
}
@Override
public void handleFailure(FxAccountClientRemoteException e) {
handleError(e);
}
@Override
public void handleSuccess(Void result) {
Toast.makeText(context, R.string.fxaccount_confirm_account_verification_link_sent, Toast.LENGTH_SHORT).show();
}
}
/**
* Resends the account verification email, and displays an appropriate
* toast on both send success and failure. Note that because the underlying implementation
* uses {@link AsyncTask}, the provided context must be UI-capable and
* this method called from the UI thread.
*
* Note that it may actually be possible to run this (and the {@link AsyncTask}) method
* from a background thread - but this hasn't been tested.
*
* @param context A UI-capable Android context.
* @param fxAccount The Firefox Account to resend the code to.
*/
public static void resendCode(Context context, AndroidFxAccount fxAccount) {
RequestDelegate<Void> delegate = new ResendCodeDelegate(context);
byte[] sessionToken;
try {
sessionToken = ((Engaged) fxAccount.getState()).getSessionToken();
} catch (Exception e) {
delegate.handleError(e);
return;
}
if (sessionToken == null) {
delegate.handleError(new IllegalStateException("sessionToken should not be null"));
return;
}
Executor executor = Executors.newSingleThreadExecutor();
FxAccountClient client = new FxAccountClient20(fxAccount.getAccountServerURI(), executor);
new FxAccountResendCodeTask(context, sessionToken, client, delegate).execute();
}
}

Просмотреть файл

@ -0,0 +1,41 @@
/* 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.tasks;
import java.io.UnsupportedEncodingException;
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.LoginResponse;
import org.mozilla.gecko.background.fxa.PasswordStretcher;
import android.content.Context;
public class FxAccountCreateAccountTask extends FxAccountSetupTask<LoginResponse> {
private static final String LOG_TAG = FxAccountCreateAccountTask.class.getSimpleName();
protected final byte[] emailUTF8;
protected final PasswordStretcher passwordStretcher;
public FxAccountCreateAccountTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, RequestDelegate<LoginResponse> delegate) throws UnsupportedEncodingException {
super(context, progressDisplay, client, delegate);
this.emailUTF8 = email.getBytes("UTF-8");
this.passwordStretcher = passwordStretcher;
}
@Override
protected InnerRequestDelegate<LoginResponse> doInBackground(Void... arg0) {
try {
client.createAccountAndGetKeys(emailUTF8, passwordStretcher, innerDelegate);
latch.await();
return innerDelegate;
} catch (Exception e) {
Logger.error(LOG_TAG, "Got exception logging in.", e);
delegate.handleError(e);
}
return null;
}
}

Просмотреть файл

@ -2,18 +2,15 @@
* 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;
package org.mozilla.gecko.fxa.tasks;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.CountDownLatch;
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.LoginResponse;
import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
import org.mozilla.gecko.background.fxa.PasswordStretcher;
import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.InnerRequestDelegate;
import org.mozilla.gecko.fxa.tasks.FxAccountSetupTask.InnerRequestDelegate;
import android.content.Context;
import android.os.AsyncTask;
@ -27,7 +24,7 @@ import android.os.AsyncTask;
* We really want to avoid making a threading mistake that brings down the whole
* process.
*/
abstract class FxAccountSetupTask<T> extends AsyncTask<Void, Void, InnerRequestDelegate<T>> {
public abstract class FxAccountSetupTask<T> extends AsyncTask<Void, Void, InnerRequestDelegate<T>> {
private static final String LOG_TAG = FxAccountSetupTask.class.getSimpleName();
public interface ProgressDisplay {
@ -117,56 +114,4 @@ abstract class FxAccountSetupTask<T> extends AsyncTask<Void, Void, InnerRequestD
latch.countDown();
}
}
public static class FxAccountCreateAccountTask extends FxAccountSetupTask<LoginResponse> {
private static final String LOG_TAG = FxAccountCreateAccountTask.class.getSimpleName();
protected final byte[] emailUTF8;
protected final PasswordStretcher passwordStretcher;
public FxAccountCreateAccountTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, RequestDelegate<LoginResponse> delegate) throws UnsupportedEncodingException {
super(context, progressDisplay, client, delegate);
this.emailUTF8 = email.getBytes("UTF-8");
this.passwordStretcher = passwordStretcher;
}
@Override
protected InnerRequestDelegate<LoginResponse> doInBackground(Void... arg0) {
try {
client.createAccountAndGetKeys(emailUTF8, passwordStretcher, innerDelegate);
latch.await();
return innerDelegate;
} catch (Exception e) {
Logger.error(LOG_TAG, "Got exception logging in.", e);
delegate.handleError(e);
}
return null;
}
}
public static class FxAccountSignInTask extends FxAccountSetupTask<LoginResponse> {
protected static final String LOG_TAG = FxAccountSignInTask.class.getSimpleName();
protected final byte[] emailUTF8;
protected final PasswordStretcher passwordStretcher;
public FxAccountSignInTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, RequestDelegate<LoginResponse> delegate) throws UnsupportedEncodingException {
super(context, progressDisplay, client, delegate);
this.emailUTF8 = email.getBytes("UTF-8");
this.passwordStretcher = passwordStretcher;
}
@Override
protected InnerRequestDelegate<LoginResponse> doInBackground(Void... arg0) {
try {
client.loginAndGetKeys(emailUTF8, passwordStretcher, innerDelegate);
latch.await();
return innerDelegate;
} catch (Exception e) {
Logger.error(LOG_TAG, "Got exception signing in.", e);
delegate.handleError(e);
}
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,41 @@
/* 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.tasks;
import java.io.UnsupportedEncodingException;
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.LoginResponse;
import org.mozilla.gecko.background.fxa.PasswordStretcher;
import android.content.Context;
public class FxAccountSignInTask extends FxAccountSetupTask<LoginResponse> {
protected static final String LOG_TAG = FxAccountSignInTask.class.getSimpleName();
protected final byte[] emailUTF8;
protected final PasswordStretcher passwordStretcher;
public FxAccountSignInTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, RequestDelegate<LoginResponse> delegate) throws UnsupportedEncodingException {
super(context, progressDisplay, client, delegate);
this.emailUTF8 = email.getBytes("UTF-8");
this.passwordStretcher = passwordStretcher;
}
@Override
protected InnerRequestDelegate<LoginResponse> doInBackground(Void... arg0) {
try {
client.loginAndGetKeys(emailUTF8, passwordStretcher, innerDelegate);
latch.await();
return innerDelegate;
} catch (Exception e) {
Logger.error(LOG_TAG, "Got exception signing in.", e);
delegate.handleError(e);
}
return null;
}
}