michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0:
michael@0: package org.mozilla.gecko.fxa.activities;
michael@0:
michael@0: import java.io.UnsupportedEncodingException;
michael@0: import java.util.concurrent.CountDownLatch;
michael@0:
michael@0: import org.mozilla.gecko.background.common.log.Logger;
michael@0: import org.mozilla.gecko.background.fxa.FxAccountClient;
michael@0: import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
michael@0: import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
michael@0: import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
michael@0: import org.mozilla.gecko.background.fxa.PasswordStretcher;
michael@0: import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.InnerRequestDelegate;
michael@0:
michael@0: import android.content.Context;
michael@0: import android.os.AsyncTask;
michael@0:
michael@0: /**
michael@0: * An AsyncTask
wrapper around signing up for, and signing in to, a
michael@0: * Firefox Account.
michael@0: *
michael@0: * It's strange to add explicit blocking to callback-threading code, but we do
michael@0: * it here to take advantage of Android's built in support for background work.
michael@0: * We really want to avoid making a threading mistake that brings down the whole
michael@0: * process.
michael@0: */
michael@0: abstract class FxAccountSetupTask extends AsyncTask> {
michael@0: private static final String LOG_TAG = FxAccountSetupTask.class.getSimpleName();
michael@0:
michael@0: public interface ProgressDisplay {
michael@0: public void showProgress();
michael@0: public void dismissProgress();
michael@0: }
michael@0:
michael@0: protected final Context context;
michael@0: protected final FxAccountClient client;
michael@0: protected final ProgressDisplay progressDisplay;
michael@0:
michael@0: // Initialized lazily.
michael@0: protected byte[] quickStretchedPW;
michael@0:
michael@0: // AsyncTask's are one-time-use, so final members are fine.
michael@0: protected final CountDownLatch latch = new CountDownLatch(1);
michael@0: protected final InnerRequestDelegate innerDelegate = new InnerRequestDelegate(latch);
michael@0:
michael@0: protected final RequestDelegate delegate;
michael@0:
michael@0: public FxAccountSetupTask(Context context, ProgressDisplay progressDisplay, FxAccountClient client, RequestDelegate delegate) {
michael@0: this.context = context;
michael@0: this.client = client;
michael@0: this.delegate = delegate;
michael@0: this.progressDisplay = progressDisplay;
michael@0: }
michael@0:
michael@0: @Override
michael@0: protected void onPreExecute() {
michael@0: if (progressDisplay != null) {
michael@0: progressDisplay.showProgress();
michael@0: }
michael@0: }
michael@0:
michael@0: @Override
michael@0: protected void onPostExecute(InnerRequestDelegate result) {
michael@0: if (progressDisplay != null) {
michael@0: progressDisplay.dismissProgress();
michael@0: }
michael@0:
michael@0: // We are on the UI thread, and need to invoke these callbacks here to allow UI updating.
michael@0: if (innerDelegate.failure != null) {
michael@0: delegate.handleFailure(innerDelegate.failure);
michael@0: } else if (innerDelegate.exception != null) {
michael@0: delegate.handleError(innerDelegate.exception);
michael@0: } else {
michael@0: delegate.handleSuccess(result.response);
michael@0: }
michael@0: }
michael@0:
michael@0: @Override
michael@0: protected void onCancelled(InnerRequestDelegate result) {
michael@0: if (progressDisplay != null) {
michael@0: progressDisplay.dismissProgress();
michael@0: }
michael@0: delegate.handleError(new IllegalStateException("Task was cancelled."));
michael@0: }
michael@0:
michael@0: protected static class InnerRequestDelegate implements RequestDelegate {
michael@0: protected final CountDownLatch latch;
michael@0: public T response = null;
michael@0: public Exception exception = null;
michael@0: public FxAccountClientRemoteException failure = null;
michael@0:
michael@0: protected InnerRequestDelegate(CountDownLatch latch) {
michael@0: this.latch = latch;
michael@0: }
michael@0:
michael@0: @Override
michael@0: public void handleError(Exception e) {
michael@0: Logger.error(LOG_TAG, "Got exception.");
michael@0: this.exception = e;
michael@0: latch.countDown();
michael@0: }
michael@0:
michael@0: @Override
michael@0: public void handleFailure(FxAccountClientRemoteException e) {
michael@0: Logger.warn(LOG_TAG, "Got failure.");
michael@0: this.failure = e;
michael@0: latch.countDown();
michael@0: }
michael@0:
michael@0: @Override
michael@0: public void handleSuccess(T result) {
michael@0: Logger.info(LOG_TAG, "Got success.");
michael@0: this.response = result;
michael@0: latch.countDown();
michael@0: }
michael@0: }
michael@0:
michael@0: public static class FxAccountCreateAccountTask extends FxAccountSetupTask {
michael@0: private static final String LOG_TAG = FxAccountCreateAccountTask.class.getSimpleName();
michael@0:
michael@0: protected final byte[] emailUTF8;
michael@0: protected final PasswordStretcher passwordStretcher;
michael@0:
michael@0: public FxAccountCreateAccountTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, RequestDelegate delegate) throws UnsupportedEncodingException {
michael@0: super(context, progressDisplay, client, delegate);
michael@0: this.emailUTF8 = email.getBytes("UTF-8");
michael@0: this.passwordStretcher = passwordStretcher;
michael@0: }
michael@0:
michael@0: @Override
michael@0: protected InnerRequestDelegate doInBackground(Void... arg0) {
michael@0: try {
michael@0: client.createAccountAndGetKeys(emailUTF8, passwordStretcher, innerDelegate);
michael@0: latch.await();
michael@0: return innerDelegate;
michael@0: } catch (Exception e) {
michael@0: Logger.error(LOG_TAG, "Got exception logging in.", e);
michael@0: delegate.handleError(e);
michael@0: }
michael@0: return null;
michael@0: }
michael@0: }
michael@0:
michael@0: public static class FxAccountSignInTask extends FxAccountSetupTask {
michael@0: protected static final String LOG_TAG = FxAccountSignInTask.class.getSimpleName();
michael@0:
michael@0: protected final byte[] emailUTF8;
michael@0: protected final PasswordStretcher passwordStretcher;
michael@0:
michael@0: public FxAccountSignInTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, RequestDelegate delegate) throws UnsupportedEncodingException {
michael@0: super(context, progressDisplay, client, delegate);
michael@0: this.emailUTF8 = email.getBytes("UTF-8");
michael@0: this.passwordStretcher = passwordStretcher;
michael@0: }
michael@0:
michael@0: @Override
michael@0: protected InnerRequestDelegate doInBackground(Void... arg0) {
michael@0: try {
michael@0: client.loginAndGetKeys(emailUTF8, passwordStretcher, innerDelegate);
michael@0: latch.await();
michael@0: return innerDelegate;
michael@0: } catch (Exception e) {
michael@0: Logger.error(LOG_TAG, "Got exception signing in.", e);
michael@0: delegate.handleError(e);
michael@0: }
michael@0: return null;
michael@0: }
michael@0: }
michael@0: }