org.ohmage.auth.AuthenticatorActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.ohmage.auth.AuthenticatorActivity.java

Source

/*
 * Copyright (C) 2013 ohmage
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ohmage.auth;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.text.TextUtils;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;

import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.GooglePlayServicesAvailabilityException;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.android.gms.plus.PlusClient;
import com.google.android.gms.plus.model.people.Person;

import org.apache.http.auth.AuthenticationException;
import org.ohmage.app.MainActivity;
import org.ohmage.app.Ohmage;
import org.ohmage.app.OhmageService;
import org.ohmage.app.OhmletActivity.OhmletFragment;
import org.ohmage.app.R;
import org.ohmage.dagger.PlusClientFragmentModule;
import org.ohmage.log.AppLogSyncAdapter;
import org.ohmage.models.AccessToken;
import org.ohmage.models.Ohmlet;
import org.ohmage.models.Ohmlet.Member;
import org.ohmage.models.Ohmlet.Role;
import org.ohmage.models.User;
import org.ohmage.operators.ContentProviderSaver;
import org.ohmage.provider.OhmageContract;
import org.ohmage.provider.ResponseContract;
import org.ohmage.streams.StreamContract;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.inject.Inject;

import retrofit.RetrofitError;
import retrofit.client.Response;
import retrofit.mime.TypedByteArray;
import rx.Observable;
import rx.schedulers.Schedulers;
import rx.util.functions.Action1;

public class AuthenticatorActivity extends AuthenticatorFragmentActivity
        implements PlusClientFragment.OnSignInListener, CreateAccountFragment.Callbacks,
        AuthenticateFragment.Callbacks, SignInFragment.Callbacks {

    @Inject
    AuthHelper auth;
    @Inject
    OhmageService ohmageService;
    @Inject
    AccountManager am;
    @Inject
    PlusClientFragment mPlusClientFragment;

    public static final int REQUEST_CODE_PLUS_CLIENT_FRAGMENT = 0;
    public static final int GOOGLE_CODE_RESULT = 1;

    private static final String TAG_ERROR_DIALOG = "error_dialog";
    private static final String TAG_OHMAGE_SIGN_IN = "sign_in_email";
    private static final String TAG_CREATE_ACCOUNT = "create_account";
    private static final String TAG_INFO_WINDOW = "info_window";
    public static final String EXTRA_HANDLE_USER_RECOVERABLE_ERROR = "extra_handle_error";

    public static final String EXTRA_CLEAR_DEFAULT_ACCOUNT = "clear_default_account";
    public static final String EXTRA_JOIN_OHMLET_ID = "extra_join_ohmlet_id";
    public static final String EXTRA_USER_INVITATION_CODE = "extra_user_invitation_code";
    public static final String EXTRA_EMAIL = "extra_email";

    private boolean mClearDefaultAccount;
    private String omhUsername;

    private AuthenticateFragment mAuthenticateFragment;

    private ArrayList<OhmageService.CancelableCallback> mNetworkCallbacks = new ArrayList<OhmageService.CancelableCallback>();

    private String mJoinOhmletId;

    /**
     * We need to listen to the back stack so we know if we should cancel network requests,
     * and if we should stop showing the spinner
     */
    private FragmentManager.OnBackStackChangedListener cancelRequests = new FragmentManager.OnBackStackChangedListener() {

        @Override
        public void onBackStackChanged() {
            FragmentManager fm = getSupportFragmentManager();

            // When we go back to the top the progress spinner should not be shown
            if (fm.getBackStackEntryCount() == 0) {
                showProgress(false);
            }

            if (fm.getBackStackEntryCount() - 1 >= 0) {
                FragmentManager.BackStackEntry entry = fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 1);

                // When we pop back to the info entry, we should cancel all network operations
                // TODO: only cancel the network operations we started using a tag
                if (TAG_INFO_WINDOW.equals(entry.getName())) {
                    for (OhmageService.CancelableCallback callback : mNetworkCallbacks) {
                        callback.cancel();
                    }
                }
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_authenticate);

        FragmentManager fm = getSupportFragmentManager();

        mAuthenticateFragment = (AuthenticateFragment) fm.findFragmentByTag("authenticate");
        // We should add the authenticate fragment the first time this activity is shown
        if (mAuthenticateFragment == null) {
            mAuthenticateFragment = new AuthenticateFragment();
            fm.beginTransaction().add(R.id.authenticate_frame, mAuthenticateFragment, "authenticate").commit();
        }

        // We handle user recoverable errors on behalf of the account authenticator
        UserRecoverableAuthException authException = (UserRecoverableAuthException) getIntent()
                .getSerializableExtra(EXTRA_HANDLE_USER_RECOVERABLE_ERROR);
        if (authException != null) {
            try {
                throw authException;
            } catch (GooglePlayServicesAvailabilityException playEx) {
                GooglePlayServicesErrorDialogFragment fragment = new GooglePlayServicesErrorDialogFragment();
                fragment.setArguments(GooglePlayServicesErrorDialogFragment
                        .createArguments(playEx.getConnectionStatusCode(), REQUEST_CODE_PLUS_CLIENT_FRAGMENT));
                showErrorDialog(fm, fragment);
            } catch (UserRecoverableAuthException userAuthEx) {
                startActivityForResult(userAuthEx.getIntent(), REQUEST_CODE_PLUS_CLIENT_FRAGMENT);
            }
            return;
        }

        // Check to see if an account already exists. If an account exists, it should only allow the
        // user to authenticate with that account since the system account doesn't change
        Account[] accounts = am.getAccountsByType(AuthUtil.ACCOUNT_TYPE);
        if (accounts.length != 0) {
            // If an account exists we should authenticate using that account
            SignInFragment signInEmailFragment = (SignInFragment) fm.findFragmentByTag(TAG_OHMAGE_SIGN_IN);

            // Create the fragment if it doesn't exist
            if (signInEmailFragment == null) {
                signInEmailFragment = new SignInFragment();
            }
            signInEmailFragment.setEmail(accounts[0].name);

            if (!signInEmailFragment.isAdded()) {
                FragmentTransaction ft = fm.beginTransaction().detach(mAuthenticateFragment);
                ft.add(R.id.sign_in_ohmage_frame, signInEmailFragment, TAG_OHMAGE_SIGN_IN).commit();
            } else {
                FragmentTransaction ft = fm.beginTransaction().detach(mAuthenticateFragment);
                ft.attach(signInEmailFragment).commit();
            }
        }

        mClearDefaultAccount = getIntent().getBooleanExtra(EXTRA_CLEAR_DEFAULT_ACCOUNT, false);

        // Check for auto join ohmlet id
        mJoinOhmletId = getIntent().getStringExtra(EXTRA_JOIN_OHMLET_ID);
    }

    @Override
    protected void onResume() {
        super.onResume();

        FragmentManager fm = getSupportFragmentManager();
        fm.addOnBackStackChangedListener(cancelRequests);
    }

    @Override
    protected void onPause() {
        super.onPause();

        FragmentManager fm = getSupportFragmentManager();
        fm.removeOnBackStackChangedListener(cancelRequests);
    }

    @Override
    public void onGoogleSignInClick() {
        showProgress(true);
        mPlusClientFragment.signIn(REQUEST_CODE_PLUS_CLIENT_FRAGMENT);
    }

    @Override
    public void onOmhSignInClick(String username, String password) {
        showProgress(true);
        omhUsername = username;

        OhmageService.CancelableCallback<AccessToken> callback = new OhmageService.CancelableCallback<AccessToken>() {
            @Override
            public void success(AccessToken accessToken, Response response) {
                if (!isCancelled()) {
                    try {
                        createAccount(omhUsername, accessToken);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void failure(RetrofitError error) {
                if (isCancelled()) {
                    return;
                }

                if (error.getResponse().getStatus() == 400) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
                                showProgress(false);
                            }
                            Toast.makeText(getBaseContext(), R.string.error_invalid_credentials, Toast.LENGTH_SHORT)
                                    .show();
                        }
                    });
                } else if (error.getResponse().getStatus() == 404) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
                                showProgress(false);
                            }
                            Toast.makeText(getBaseContext(), R.string.error_server_not_found, Toast.LENGTH_SHORT)
                                    .show();
                        }
                    });
                } else {
                    onRetrofitError(error);
                }
            }
        };
        ohmageService.getAccessTokenWithOmhUserPassword("password", username, password, callback);
    }

    @Override
    protected void onActivityResult(int requestCode, int responseCode, Intent intent) {
        switch (requestCode) {
        case GOOGLE_CODE_RESULT:
            if (responseCode == RESULT_OK) {
                String token = intent.getStringExtra("authtoken");
                if (token != null) {
                    useGoogleToken(token, false);
                    return;
                }
            }
        case REQUEST_CODE_PLUS_CLIENT_FRAGMENT:
            // Only show progress if the result failed indicating there was an error
            showProgress(responseCode == RESULT_OK);
            mPlusClientFragment.handleOnActivityResult(requestCode, responseCode, intent);
            break;
        }
    }

    @Override
    public void onSignedIn(PlusClient plusClient) {
        // If the user just logged out, their google auth credentials will still be logged in
        // so we need to always clear the first account by default
        if (mClearDefaultAccount) {
            mClearDefaultAccount = false;
            plusClient.clearDefaultAccount();
        } else {
            startLogin(plusClient);
        }
    }

    @Override
    public void onSignInFailed() {
        showProgress(false);
        // There was no default account
        mClearDefaultAccount = false;
    }

    /**
     * Called when the user clicks the create account button if we are performing an action
     * which requires the parent activity to fetch the token. The parent activity must
     * call {@link org.ohmage.auth.CreateAccountFragment.UseToken#useToken(String)} with the token.
     */
    @Override
    public void fetchToken(final CreateAccountFragment.UseToken callback) {
        new GoogleAccessTokenTask(mPlusClientFragment.getClient().getAccountName(),
                new GoogleAccessTokenCallback() {
                    @Override
                    public void onGoogleAccessTokenReceived(String token) {
                        callback.useToken(token);
                    }
                }).execute();
    }

    /**
     * Called when the account is actually being created
     */
    @Override
    public void onCreateAccount() {
        hideKeyboard();
        // Hide the create account fragment and show the progress spinner
        hideFragment(TAG_CREATE_ACCOUNT);
    }

    /**
     * Called when the account is being signed in from the E-mail fragment
     */
    @Override
    public void onAccountSignInOhmage() {
        hideKeyboard();
        // Hide the sign in fragment
        hideFragment(TAG_OHMAGE_SIGN_IN);
    }

    /**
     * Show the sign in fragment
     */
    private void showSignInFragment() {
        FragmentManager fm = getSupportFragmentManager();
        SignInFragment signInOhmageFragment = (SignInFragment) fm.findFragmentByTag(TAG_OHMAGE_SIGN_IN);

        // Create the fragment if it doesn't exist
        if (signInOhmageFragment == null) {
            signInOhmageFragment = new SignInFragment();
        }

        // Add the fragment
        addFragment(R.id.sign_in_ohmage_frame, signInOhmageFragment, TAG_OHMAGE_SIGN_IN);
    }

    /**
     * Show the create account fragment
     *
     * @param grantType
     * @param fullName
     */
    private void showCreateAccountFragment(AuthUtil.GrantType grantType, String fullName) {
        FragmentManager fm = getSupportFragmentManager();
        CreateAccountFragment createAccountFragment = (CreateAccountFragment) fm
                .findFragmentByTag(TAG_CREATE_ACCOUNT);

        // Create the fragment if it doesn't exist
        if (createAccountFragment == null) {
            createAccountFragment = new CreateAccountFragment();
        }

        // Set the parameters for this fragment
        createAccountFragment.setGrantType(grantType);
        createAccountFragment.setFullName(fullName);

        // If an email exists, send that
        if (getIntent().hasExtra(EXTRA_EMAIL))
            createAccountFragment.setEmail(getIntent().getStringExtra(EXTRA_EMAIL));

        // Add the fragment
        addFragment(R.id.create_account_frame, createAccountFragment, TAG_CREATE_ACCOUNT);
    }

    /**
     * Shows the fragment with the correct transition and back stack
     *
     * @param fragment
     */
    private void showFragment(Fragment fragment) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction().detach(mAuthenticateFragment);
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).addToBackStack(TAG_INFO_WINDOW).attach(fragment)
                .commit();
    }

    /**
     * Adds the fragmetn if it doesn't exist and shows it with the correct transition and
     * back stack
     *
     * @param id
     * @param fragment
     * @param tag
     */
    private void addFragment(int id, Fragment fragment, String tag) {
        if (!fragment.isAdded()) {
            FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction().detach(mAuthenticateFragment);
            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).addToBackStack(TAG_INFO_WINDOW)
                    .add(id, fragment, tag).commit();
        } else {
            showFragment(fragment);
        }
    }

    /**
     * Hides a fragment by detaching it from the hierarchy. This allows it to keep its state,
     * so if it is attached later it won't lose anything.
     *
     * @param tag
     */
    private void hideFragment(String tag) {
        showProgress(true);

        FragmentManager fm = getSupportFragmentManager();
        Fragment fragment = fm.findFragmentByTag(tag);
        fm.beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE).detach(fragment)
                .attach(mAuthenticateFragment).addToBackStack(null).commit();
    }

    /**
     * Shows the progress UI and hides the authentication buttons.
     */
    private void showProgress(final boolean show) {
        mAuthenticateFragment.showProgress(show);
    }

    private void useGoogleToken(final String token, final boolean shouldRetry) {
        OhmageService.CancelableCallback<AccessToken> callback = new OhmageService.CancelableCallback<AccessToken>() {
            @Override
            public void success(AccessToken accessToken, Response response) {
                if (!isCancelled()) {
                    createAccount(mPlusClientFragment.getClient().getAccountName(), accessToken);
                }
            }

            @Override
            public void failure(RetrofitError error) {
                if (isCancelled()) {
                    return;
                }

                if (!Ohmage.USE_DSU_DATAPOINTS_API && error.getResponse().getStatus() == 409) {
                    // won't happen for OMH DSU
                    Person person = mPlusClientFragment.getClient().getCurrentPerson();
                    String fullName = null;
                    if (person != null) {
                        fullName = person.getDisplayName();
                    }
                    showCreateAccountFragment(AuthUtil.GrantType.GOOGLE_OAUTH2, fullName);
                } else if (error.getResponse().getStatus() == 400 && shouldRetry) {
                    GoogleAuthUtil.invalidateToken(getApplicationContext(), token.substring(8));
                    startLogin(mPlusClientFragment.getClient());
                } else {
                    onRetrofitError(error);
                }
            }
        };
        ohmageService.getAccessTokenWithGoogleAccessToken(AuthUtil.OMH_CLIENT_ID, AuthUtil.OMH_CLIENT_SECRET, token,
                callback);
    }

    /**
     * Starts the process of getting an auth_token from the server
     *
     * @param plusClient
     */
    private void startLogin(final PlusClient plusClient) {
        final String email = plusClient.getAccountName();
        new GoogleAccessTokenTask(email, new GoogleAccessTokenCallback() {
            @Override
            public void onGoogleAccessTokenReceived(String token) {
                // Now that we have a google accessToken, we can make a request to get one from ohmage
                useGoogleToken(token, true);
            }
        }).execute();
    }

    public void createAccount(String email, AccessToken token) {
        // Join ohmlet if applicable
        if (mJoinOhmletId != null)
            autoJoinOhmlet(token.getUserId());

        // Add the account or find an existing account
        Account account = addOrFindAccount(email, token.getRefreshToken());

        if (mPlusClientFragment.getClient() != null && mPlusClientFragment.getClient().isConnected()) {
            String googleAccountName = mPlusClientFragment.getClient().getAccountName();
            am.setUserData(account, Authenticator.USER_DATA_GOOGLE_ACCOUNT, googleAccountName);
        }

        am.setUserData(account, Authenticator.USE_PASSWORD, String.valueOf(false));
        am.setUserData(account, Authenticator.USER_ID, token.getUserId());
        am.setAuthToken(account, AuthUtil.AUTHTOKEN_TYPE, token.getAccessToken());

        finishAccountAdd(email, token.getAccessToken(), token.getRefreshToken());
    }

    public void createAccount(User user, String password) {
        // Join ohmlet if applicable
        if (mJoinOhmletId != null) {
            if (user.registration != null) {
                autoJoinOhmlet(user.registration.userId);
            } else {
                autoJoinOhmlet("me");
            }
        }

        // Add the account or find an existing account
        Account account = addOrFindAccount(user.email, password);

        // Since we are adding the user with the password this account has not been activated
        am.setUserData(account, Authenticator.USE_PASSWORD, String.valueOf(true));

        // Determine the userId for this user if we can
        if (user.registration != null) {
            am.setUserData(account, Authenticator.USER_ID, user.registration.userId);

            // Join ohmlet if applicable
            if (mJoinOhmletId != null)
                autoJoinOhmlet(user.registration.userId);
        }

        finishAccountAdd(user.email, null, password);
    }

    private Account addOrFindAccount(String email, String password) {
        Account[] accounts = am.getAccountsByType(AuthUtil.ACCOUNT_TYPE);
        Account account = accounts.length != 0 ? accounts[0] : new Account(email, AuthUtil.ACCOUNT_TYPE);

        if (accounts.length == 0) {
            am.addAccountExplicitly(account, password, null);

            // Turn on automatic syncing for this account
            ContentResolver.setSyncAutomatically(account, OhmageContract.CONTENT_AUTHORITY, true);
            ContentResolver.addPeriodicSync(account, StreamContract.CONTENT_AUTHORITY, new Bundle(),
                    AuthUtil.SYNC_INTERVAL);

            ContentResolver.setSyncAutomatically(account, StreamContract.CONTENT_AUTHORITY, true);
            ContentResolver.addPeriodicSync(account, StreamContract.CONTENT_AUTHORITY, new Bundle(),
                    AuthUtil.SYNC_INTERVAL);

            ContentResolver.setSyncAutomatically(account, ResponseContract.CONTENT_AUTHORITY, true);
            ContentResolver.addPeriodicSync(account, ResponseContract.CONTENT_AUTHORITY, new Bundle(),
                    AuthUtil.SYNC_INTERVAL);

            ContentResolver.setSyncAutomatically(account, AppLogSyncAdapter.CONTENT_AUTHORITY, true);
            ContentResolver.addPeriodicSync(account, AppLogSyncAdapter.CONTENT_AUTHORITY, new Bundle(),
                    AuthUtil.SYNC_INTERVAL);
        } else {
            am.setPassword(accounts[0], password);
        }
        return account;
    }

    private void finishAccountAdd(String accountName, String authToken, String password) {
        final Intent intent = new Intent();
        intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, accountName);
        intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AuthUtil.ACCOUNT_TYPE);
        if (authToken != null)
            intent.putExtra(AccountManager.KEY_AUTHTOKEN, authToken);
        intent.putExtra(AccountManager.KEY_PASSWORD, password);
        setAccountAuthenticatorResult(intent.getExtras());
        setResult(RESULT_OK, intent);
        finish();

        if (!calledByAuthenticator())
            startActivity(new Intent(getBaseContext(), MainActivity.class));
    }

    private void autoJoinOhmlet(String userId) {
        Ohmlet o = new Ohmlet();
        o.name = "ohmlet";
        o.ohmletId = mJoinOhmletId;
        o.people = new Member.List();
        Member m = new Member();
        m.memberId = TextUtils.isEmpty(userId) ? "me" : userId;
        m.role = Role.MEMBER;
        m.code = getIntent().getStringExtra(OhmletFragment.EXTRA_OHMLET_INVITATION_ID);
        o.people.add(m);
        o.dirty = true;

        Observable.from(o).subscribeOn(Schedulers.io()).doOnNext(new ContentProviderSaver())
                .doOnError(new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        throwable.printStackTrace();
                    }
                }).subscribe();
    }

    /**
     * This function should be called on a retrofit error
     *
     * @param error
     */
    public void onRetrofitError(final RetrofitError error) {

        // If there is a network error, we should pop back to the last info window if there was one
        getSupportFragmentManager().popBackStack(TAG_INFO_WINDOW, 0);

        // If there is nothing on the back stack to pop we should immediately stop showing progress
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
                    showProgress(false);
                }

                Response r = error.getResponse();

                if (error.isNetworkError()) {
                    Toast.makeText(getBaseContext(), R.string.network_error, Toast.LENGTH_SHORT).show();
                } else if (error.getCause() instanceof AuthenticationException) {
                    Toast.makeText(getBaseContext(), R.string.error_invalid_credentials, Toast.LENGTH_SHORT).show();
                } else if (r != null && r.getBody() instanceof TypedByteArray) {
                    String body = new String(((TypedByteArray) r.getBody()).getBytes());
                    Toast.makeText(getBaseContext(), body, Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(getBaseContext(), R.string.unknown_error, Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

    private void hideKeyboard() {
        if (getCurrentFocus() != null) {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
        }
    }

    public static interface GoogleAccessTokenCallback {
        void onGoogleAccessTokenReceived(String token);
    }

    /**
     * This AsyncTask gets an access token from google
     */
    private class GoogleAccessTokenTask extends AsyncTask<Void, Void, String> {

        private final GoogleAccessTokenCallback mCallback;

        private final String mAccountName;

        public GoogleAccessTokenTask(String accountName, GoogleAccessTokenCallback callback) {
            mAccountName = accountName;
            mCallback = callback;
        }

        public void onPreExecute() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    showProgress(true);
                }
            });
        }

        @Override
        protected String doInBackground(Void... ignore) {
            return getGoogleAccessTokenBlocking(mAccountName);
        }

        @Override
        protected void onPostExecute(String token) {
            super.onPostExecute(token);
            if (!TextUtils.isEmpty(token) && mCallback != null) {
                mCallback.onGoogleAccessTokenReceived(token);
            }
        }

        private String getGoogleAccessTokenBlocking(String accountName) {
            try {
                return auth.googleAuthGetAccessToken(accountName);
            } catch (GooglePlayServicesAvailabilityException playEx) {
                GooglePlayServicesErrorDialogFragment fragment = new GooglePlayServicesErrorDialogFragment();
                fragment.setArguments(GooglePlayServicesErrorDialogFragment
                        .createArguments(playEx.getConnectionStatusCode(), GOOGLE_CODE_RESULT));
                showErrorDialog(getSupportFragmentManager(), fragment);
            } catch (UserRecoverableAuthException userAuthEx) {
                startActivityForResult(userAuthEx.getIntent(), GOOGLE_CODE_RESULT);
            } catch (IOException transientEx) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getBaseContext(), getString(R.string.network_error), Toast.LENGTH_SHORT)
                                .show();
                    }
                });
            } catch (GoogleAuthException authEx) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getBaseContext(), getString(R.string.account_error), Toast.LENGTH_SHORT)
                                .show();
                    }
                });
            }
            return null;
        }
    }

    /**
     * Shows the error dialog if there is one from {@link GoogleAuthUtil}
     *
     * @param errorDialog
     */
    public static void showErrorDialog(FragmentManager fragmentManager, DialogFragment errorDialog) {
        DialogFragment oldErrorDialog = (DialogFragment) fragmentManager.findFragmentByTag(TAG_ERROR_DIALOG);
        if (oldErrorDialog != null) {
            oldErrorDialog.dismiss();
        }

        errorDialog.show(fragmentManager, TAG_ERROR_DIALOG);
    }

    @Override
    protected List<Object> getModules() {
        return Arrays.<Object>asList(new PlusClientFragmentModule(this));
    }

}