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: }