/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * 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 com.android.email.activity.setup;

import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.app.ActionBar;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.LoaderManager;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.CursorLoader;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;

import com.android.email.R;
import com.android.email.setup.AuthenticatorSetupIntentHelper;
import com.android.email.service.EmailServiceUtils;
import com.android.emailcommon.VendorPolicyLoader;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.service.SyncWindow;
import com.android.mail.analytics.Analytics;
import com.android.mail.providers.MailAppProvider;
import com.android.mail.providers.UIProvider;
import com.android.mail.utils.LogUtils;

import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

public class AccountSetupFinal extends AccountSetupActivity
        implements AccountFinalizeFragment.Callback,
        AccountSetupNoteDialogFragment.Callback, AccountCreationFragment.Callback,
        AccountCheckSettingsFragment.Callback, SecurityRequiredDialogFragment.Callback,
        CheckSettingsErrorDialogFragment.Callback, CheckSettingsProgressDialogFragment.Callback,
        AccountSetupTypeFragment.Callback, AccountSetupNamesFragment.Callback,
        AccountSetupOptionsFragment.Callback, AccountSetupBasicsFragment.Callback,
        AccountServerBaseFragment.Callback, AccountSetupCredentialsFragment.Callback,
        DuplicateAccountDialogFragment.Callback, AccountSetupABFragment.Callback {

    /**
     * Direct access for forcing account creation
     * For use by continuous automated test system (e.g. in conjunction with monkey tests)
     *
     * === Support for automated testing ==
     * This activity can also be launched directly via INTENT_FORCE_CREATE_ACCOUNT. This is intended
     * only for use by continuous test systems, and is currently only available when
     * {@link ActivityManager#isRunningInTestHarness()} is set.  To use this mode, you must
     * construct an intent which contains all necessary information to create the account.  No
     * connection checking is done, so the account may or may not actually work.  Here is a sample
     * command, for a gmail account "test_account" with a password of "test_password".
     *
     *      $ adb shell am start -a com.android.email.FORCE_CREATE_ACCOUNT \
     *          -e EMAIL test_account@gmail.com \
     *          -e USER "Test Account Name" \
     *          -e INCOMING imap+ssl+://test_account:test_password@imap.gmail.com \
     *          -e OUTGOING smtp+ssl+://test_account:test_password@smtp.gmail.com
     *
     * Note: For accounts that require the full email address in the login, encode the @ as %40.
     * Note: Exchange accounts that require device security policies cannot be created
     * automatically.
     *
     * For accounts that correspond to services in providers.xml you can also use the following form
     *
     *      $adb shell am start -a com.android.email.FORCE_CREATE_ACCOUNT \
     *          -e EMAIL test_account@gmail.com \
     *          -e PASSWORD test_password
     *
     * and the appropriate incoming/outgoing information will be filled in automatically.
     */
    private static String INTENT_FORCE_CREATE_ACCOUNT;
    private static final String EXTRA_CREATE_ACCOUNT_EMAIL = "EMAIL";
    private static final String EXTRA_CREATE_ACCOUNT_USER = "USER";
    private static final String EXTRA_CREATE_ACCOUNT_PASSWORD = "PASSWORD";
    private static final String EXTRA_CREATE_ACCOUNT_INCOMING = "INCOMING";
    private static final String EXTRA_CREATE_ACCOUNT_OUTGOING = "OUTGOING";
    private static final String EXTRA_CREATE_ACCOUNT_SYNC_LOOKBACK = "SYNC_LOOKBACK";

    private static final String CREATE_ACCOUNT_SYNC_ALL_VALUE = "ALL";

    private static final Boolean DEBUG_ALLOW_NON_TEST_HARNESS_CREATION = false;

    protected static final String ACTION_JUMP_TO_INCOMING = "jumpToIncoming";
    protected static final String ACTION_JUMP_TO_OUTGOING = "jumpToOutgoing";
    protected static final String ACTION_JUMP_TO_OPTIONS = "jumpToOptions";

    private static final String SAVESTATE_KEY_IS_PROCESSING = "AccountSetupFinal.is_processing";
    private static final String SAVESTATE_KEY_STATE = "AccountSetupFinal.state";
    private static final String SAVESTATE_KEY_PROVIDER = "AccountSetupFinal.provider";
    private static final String SAVESTATE_KEY_AUTHENTICATOR_RESPONSE = "AccountSetupFinal.authResp";
    private static final String SAVESTATE_KEY_REPORT_AUTHENTICATOR_ERROR =
            "AccountSetupFinal.authErr";
    private static final String SAVESTATE_KEY_IS_PRE_CONFIGURED = "AccountSetupFinal.preconfig";
    private static final String SAVESTATE_KEY_SKIP_AUTO_DISCOVER = "AccountSetupFinal.noAuto";
    private static final String SAVESTATE_KEY_PASSWORD_FAILED = "AccountSetupFinal.passwordFailed";

    private static final String CONTENT_FRAGMENT_TAG = "AccountSetupContentFragment";
    private static final String CREDENTIALS_BACKSTACK_TAG = "AccountSetupCredentialsFragment";

    // Collecting initial email and password
    private static final int STATE_BASICS = 0;
    // Show the user some interstitial message after email entry
    private static final int STATE_BASICS_POST = 1;
    // Account is not pre-configured, query user for account type
    private static final int STATE_TYPE = 2;
    // Account is pre-configured, but the user picked a different protocol
    private static final int STATE_AB = 3;
    // Collect initial password or oauth token
    private static final int STATE_CREDENTIALS = 4;
    // Account is a pre-configured account, run the checker
    private static final int STATE_CHECKING_PRECONFIGURED = 5;
    // Auto-discovering exchange account info, possibly other protocols later
    private static final int STATE_AUTO_DISCOVER = 6;
    // User is entering incoming settings
    private static final int STATE_MANUAL_INCOMING = 7;
    // We're checking incoming settings
    private static final int STATE_CHECKING_INCOMING = 8;
    // User is entering outgoing settings
    private static final int STATE_MANUAL_OUTGOING = 9;
    // We're checking outgoing settings
    private static final int STATE_CHECKING_OUTGOING = 10;
    // User is entering sync options
    private static final int STATE_OPTIONS = 11;
    // We're creating the account
    private static final int STATE_CREATING = 12;
    // User is entering account name and real name
    private static final int STATE_NAMES = 13;
    // we're finalizing the account
    private static final int STATE_FINALIZE = 14;

    private int mState = STATE_BASICS;

    private boolean mIsProcessing = false;
    private boolean mForceCreate = false;
    private boolean mReportAccountAuthenticatorError;
    private AccountAuthenticatorResponse mAccountAuthenticatorResponse;
    // True if this provider is found in our providers.xml, set after Basics
    private boolean mIsPreConfiguredProvider = false;
    // True if the user selected manual setup
    private boolean mSkipAutoDiscover = false;
    // True if validating the pre-configured provider failed and we want manual setup
    private boolean mPreConfiguredFailed = false;

    private VendorPolicyLoader.Provider mProvider;
    private boolean mPasswordFailed;

    private static final int OWNER_NAME_LOADER_ID = 0;
    private String mOwnerName;

    private static final int EXISTING_ACCOUNTS_LOADER_ID = 1;
    private Map<String, String> mExistingAccountsMap;

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

        final Intent intent = getIntent();
        final String action = intent.getAction();

        if (INTENT_FORCE_CREATE_ACCOUNT == null) {
            INTENT_FORCE_CREATE_ACCOUNT = getString(R.string.intent_force_create_email_account);
        }

        setContentView(R.layout.account_setup_activity);

        final ActionBar actionBar = getActionBar();
        if (actionBar != null) {
            // Hide the app icon.
            actionBar.setIcon(android.R.color.transparent);
            actionBar.setDisplayUseLogoEnabled(false);
        }

        if (savedInstanceState != null) {
            mIsProcessing = savedInstanceState.getBoolean(SAVESTATE_KEY_IS_PROCESSING, false);
            mState = savedInstanceState.getInt(SAVESTATE_KEY_STATE, STATE_OPTIONS);
            mProvider = (VendorPolicyLoader.Provider)
                    savedInstanceState.getSerializable(SAVESTATE_KEY_PROVIDER);
            mAccountAuthenticatorResponse =
                    savedInstanceState.getParcelable(SAVESTATE_KEY_AUTHENTICATOR_RESPONSE);
            mReportAccountAuthenticatorError =
                    savedInstanceState.getBoolean(SAVESTATE_KEY_REPORT_AUTHENTICATOR_ERROR);
            mIsPreConfiguredProvider =
                    savedInstanceState.getBoolean(SAVESTATE_KEY_IS_PRE_CONFIGURED);
            mSkipAutoDiscover = savedInstanceState.getBoolean(SAVESTATE_KEY_SKIP_AUTO_DISCOVER);
            mPasswordFailed = savedInstanceState.getBoolean(SAVESTATE_KEY_PASSWORD_FAILED);
        } else {
            // If we're not restoring from a previous state, we want to configure the initial screen

            // Set aside incoming AccountAuthenticatorResponse, if there was any
            mAccountAuthenticatorResponse = getIntent()
                    .getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
            if (mAccountAuthenticatorResponse != null) {
                // When this Activity is called as part of account authentification flow,
                // we are responsible for eventually reporting the result (success or failure) to
                // the account manager.  Most exit paths represent an failed or abandoned setup,
                // so the default is to report the error.  Success will be reported by the code in
                // AccountSetupOptions that commits the finally created account.
                mReportAccountAuthenticatorError = true;
            }

            // Initialize the SetupDataFragment
            if (INTENT_FORCE_CREATE_ACCOUNT.equals(action)) {
                mSetupData.setFlowMode(AuthenticatorSetupIntentHelper.FLOW_MODE_FORCE_CREATE);
            } else {
                final int intentFlowMode = intent.getIntExtra(
                        AuthenticatorSetupIntentHelper.EXTRA_FLOW_MODE,
                        AuthenticatorSetupIntentHelper.FLOW_MODE_UNSPECIFIED);
                final String flowAccountType = intent.getStringExtra(
                        AuthenticatorSetupIntentHelper.EXTRA_FLOW_ACCOUNT_TYPE);
                mSetupData.setAmProtocol(
                        EmailServiceUtils.getProtocolFromAccountType(this, flowAccountType));
                mSetupData.setFlowMode(intentFlowMode);
            }

            mState = STATE_BASICS;
            // Support unit testing individual screens
            if (TextUtils.equals(ACTION_JUMP_TO_INCOMING, action)) {
                mState = STATE_MANUAL_INCOMING;
            } else if (TextUtils.equals(ACTION_JUMP_TO_OUTGOING, action)) {
                mState = STATE_MANUAL_OUTGOING;
            } else if (TextUtils.equals(ACTION_JUMP_TO_OPTIONS, action)) {
                mState = STATE_OPTIONS;
            }
            updateContentFragment(false /* addToBackstack */);
            mPasswordFailed = false;
        }

        if (!mIsProcessing && mSetupData.getFlowMode() ==
                AuthenticatorSetupIntentHelper.FLOW_MODE_FORCE_CREATE) {
            /**
             * To support continuous testing, we allow the forced creation of accounts.
             * This works in a manner fairly similar to automatic setup, in which the complete
             * server Uri's are available, except that we will also skip checking (as if both
             * checks were true) and all other UI.
             *
             * email: The email address for the new account
             * user: The user name for the new account
             * incoming: The URI-style string defining the incoming account
             * outgoing: The URI-style string defining the outgoing account
             */
            final String email = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_EMAIL);
            final String user = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_USER);
            final String password = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_PASSWORD);
            final String incoming = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_INCOMING);
            final String outgoing = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_OUTGOING);
            final String syncLookbackText = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_SYNC_LOOKBACK);
            final int syncLookback;
            if (TextUtils.equals(syncLookbackText, CREATE_ACCOUNT_SYNC_ALL_VALUE)) {
                syncLookback = SyncWindow.SYNC_WINDOW_ALL;
            } else {
                syncLookback = -1;
            }
            // If we've been explicitly provided with all the details to fill in the account, we
            // can use them
            final boolean explicitForm = !(TextUtils.isEmpty(user) ||
                    TextUtils.isEmpty(incoming) || TextUtils.isEmpty(outgoing));
            // If we haven't been provided the details, but we have the password, we can look up
            // the info from providers.xml
            final boolean implicitForm = !TextUtils.isEmpty(password) && !explicitForm;
            if (TextUtils.isEmpty(email) || !(explicitForm || implicitForm)) {
                LogUtils.e(LogUtils.TAG, "Force account create requires extras EMAIL, " +
                        "USER, INCOMING, OUTGOING, or EMAIL and PASSWORD");
                finish();
                return;
            }

            if (implicitForm) {
                final String[] emailParts = email.split("@");
                final String domain = emailParts[1].trim();
                mProvider = AccountSettingsUtils.findProviderForDomain(this, domain);
                if (mProvider == null) {
                    LogUtils.e(LogUtils.TAG, "findProviderForDomain couldn't find provider");
                    finish();
                    return;
                }
                mIsPreConfiguredProvider = true;
                mSetupData.setEmail(email);
                boolean autoSetupCompleted = finishAutoSetup();
                if (!autoSetupCompleted) {
                    LogUtils.e(LogUtils.TAG, "Force create account failed to create account");
                    finish();
                    return;
                }
                final Account account = mSetupData.getAccount();
                final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
                recvAuth.mPassword = password;
                final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
                sendAuth.mPassword = password;
            } else {
                final Account account = mSetupData.getAccount();

                try {
                    final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
                    recvAuth.setHostAuthFromString(incoming);

                    final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
                    sendAuth.setHostAuthFromString(outgoing);
                } catch (URISyntaxException e) {
                    // If we can't set up the URL, don't continue
                    Toast.makeText(this, R.string.account_setup_username_password_toast,
                            Toast.LENGTH_LONG)
                            .show();
                    finish();
                    return;
                }

                populateSetupData(user, email);
                // We need to do this after calling populateSetupData(), because that will
                // overwrite it with the default values.
                if (syncLookback >= SyncWindow.SYNC_WINDOW_ACCOUNT &&
                    syncLookback <= SyncWindow.SYNC_WINDOW_ALL) {
                    account.mSyncLookback = syncLookback;
                }
            }

            mState = STATE_OPTIONS;
            updateContentFragment(false /* addToBackstack */);
            getFragmentManager().executePendingTransactions();

            if (!DEBUG_ALLOW_NON_TEST_HARNESS_CREATION &&
                    !ActivityManager.isRunningInTestHarness()) {
                LogUtils.e(LogUtils.TAG,
                        "ERROR: Force account create only allowed while in test harness");
                finish();
                return;
            }

            mForceCreate = true;
        }

        // Launch a loader to look up the owner name.  It should be ready well in advance of
        // the time the user clicks next or manual.
        getLoaderManager().initLoader(OWNER_NAME_LOADER_ID, null, new OwnerNameLoaderCallbacks());

        // Launch a loader to cache some info about existing accounts so we can dupe-check against
        // them.
        getLoaderManager().initLoader(EXISTING_ACCOUNTS_LOADER_ID, null,
                new ExistingAccountsLoaderCallbacks());
    }

    private class OwnerNameLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
        @Override
        public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
            return new CursorLoader(AccountSetupFinal.this,
                    ContactsContract.Profile.CONTENT_URI,
                    new String[] {ContactsContract.Profile.DISPLAY_NAME_PRIMARY},
                    null, null, null);
        }

        @Override
        public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) {
            if (data != null && data.moveToFirst()) {
                mOwnerName = data.getString(data.getColumnIndex(
                        ContactsContract.Profile.DISPLAY_NAME_PRIMARY));
            }
        }

        @Override
        public void onLoaderReset(final Loader<Cursor> loader) {}
    }

    private class ExistingAccountsLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
        @Override
        public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
            return new CursorLoader(AccountSetupFinal.this, MailAppProvider.getAccountsUri(),
                    new String[] {UIProvider.AccountColumns.ACCOUNT_MANAGER_NAME,
                            UIProvider.AccountColumns.NAME},
                    null, null, null);
        }

        @Override
        public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) {
            if (data == null || !data.moveToFirst()) {
                mExistingAccountsMap = null;
                return;
            }

            mExistingAccountsMap = new HashMap<>();

            final int emailColumnIndex = data.getColumnIndex(
                    UIProvider.AccountColumns.ACCOUNT_MANAGER_NAME);
            final int displayNameColumnIndex =
                    data.getColumnIndex(UIProvider.AccountColumns.NAME);

            do {
                final String email = data.getString(emailColumnIndex);
                final String displayName = data.getString(displayNameColumnIndex);
                mExistingAccountsMap.put(email,
                        TextUtils.isEmpty(displayName) ? email : displayName);
            } while (data.moveToNext());
        }

        @Override
        public void onLoaderReset(final Loader<Cursor> loader) {
            mExistingAccountsMap = null;
        }
    }

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

        Analytics.getInstance().activityStart(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mForceCreate) {
            mForceCreate = false;

            // We need to do this after onCreate so that we can ensure that the fragment is
            // fully created before querying it.
            // This will call initiateAccountCreation() for us
            proceed();
        }
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(SAVESTATE_KEY_IS_PROCESSING, mIsProcessing);
        outState.putInt(SAVESTATE_KEY_STATE, mState);
        outState.putSerializable(SAVESTATE_KEY_PROVIDER, mProvider);
        outState.putParcelable(SAVESTATE_KEY_AUTHENTICATOR_RESPONSE, mAccountAuthenticatorResponse);
        outState.putBoolean(SAVESTATE_KEY_REPORT_AUTHENTICATOR_ERROR,
                mReportAccountAuthenticatorError);
        outState.putBoolean(SAVESTATE_KEY_IS_PRE_CONFIGURED, mIsPreConfiguredProvider);
        outState.putBoolean(SAVESTATE_KEY_PASSWORD_FAILED, mPasswordFailed);
    }

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

        Analytics.getInstance().activityStop(this);
    }

    /**
     * Swap in the new fragment according to mState. This pushes the current fragment onto the back
     * stack, so only call it once per transition.
     */
    private void updateContentFragment(boolean addToBackstack) {
        final AccountSetupFragment f;
        String backstackTag = null;

        switch (mState) {
            case STATE_BASICS:
                f = AccountSetupBasicsFragment.newInstance();
                break;
            case STATE_TYPE:
                f = AccountSetupTypeFragment.newInstance();
                break;
            case STATE_AB:
                f = AccountSetupABFragment.newInstance(mSetupData.getEmail(),
                        mSetupData.getAmProtocol(), mSetupData.getIncomingProtocol(this));
                break;
            case STATE_CREDENTIALS:
                f = AccountSetupCredentialsFragment.newInstance(mSetupData.getEmail(),
                        mSetupData.getIncomingProtocol(this), mSetupData.getClientCert(this),
                        mPasswordFailed, false /* standalone */);
                backstackTag = CREDENTIALS_BACKSTACK_TAG;
                break;
            case STATE_MANUAL_INCOMING:
                f = AccountSetupIncomingFragment.newInstance(false);
                break;
            case STATE_MANUAL_OUTGOING:
                f = AccountSetupOutgoingFragment.newInstance(false);
                break;
            case STATE_OPTIONS:
                f = AccountSetupOptionsFragment.newInstance();
                break;
            case STATE_NAMES:
                f = AccountSetupNamesFragment.newInstance();
                break;
            default:
                throw new IllegalStateException("Incorrect state " + mState);
        }
        f.setState(mState);
        final FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.setCustomAnimations(R.anim.fade_in, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out);
        ft.replace(R.id.setup_fragment_container, f, CONTENT_FRAGMENT_TAG);
        if (addToBackstack) {
            ft.addToBackStack(backstackTag);
        }
        ft.commit();

        final InputMethodManager imm =
                (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
        final View fragment_container = findViewById(R.id.setup_fragment_container);
        imm.hideSoftInputFromWindow(fragment_container.getWindowToken(),
                0 /* flags: always hide */);
    }

    /**
     * Retrieve the current content fragment
     * @return The content fragment or null if it wasn't found for some reason
     */
    private AccountSetupFragment getContentFragment() {
        return (AccountSetupFragment) getFragmentManager().findFragmentByTag(CONTENT_FRAGMENT_TAG);
    }

    /**
     * Reads the flow state saved into the current fragment and restores mState to it, also
     * resetting the headline at the same time.
     */
    private void resetStateFromCurrentFragment() {
        AccountSetupFragment f = getContentFragment();
        mState = f.getState();
    }

    /**
     * Main choreography function to handle moving forward through scenes. Moving back should be
     * generally handled for us by the back stack
     */
    protected void proceed() {
        mIsProcessing = false;
        final AccountSetupFragment oldContentFragment = getContentFragment();
        if (oldContentFragment != null) {
            oldContentFragment.setNextButtonEnabled(true);
        }

        getFragmentManager().executePendingTransactions();

        switch (mState) {
            case STATE_BASICS:
                final boolean advance = onBasicsComplete();
                if (!advance) {
                    mState = STATE_BASICS_POST;
                    break;
                } // else fall through
            case STATE_BASICS_POST:
                if (shouldDivertToManual()) {
                    mSkipAutoDiscover = true;
                    mIsPreConfiguredProvider = false;
                    mState = STATE_TYPE;
                } else {
                    mSkipAutoDiscover = false;
                    if (mIsPreConfiguredProvider) {
                        if (!TextUtils.isEmpty(mSetupData.getAmProtocol()) &&
                                !TextUtils.equals(mSetupData.getAmProtocol(),
                                        mSetupData.getIncomingProtocol(this))) {
                            mState = STATE_AB;
                        } else {
                            mState = STATE_CREDENTIALS;
                            if (possiblyDivertToGmail()) {
                                return;
                            }
                        }
                    } else {
                        final String amProtocol = mSetupData.getAmProtocol();
                        if (!TextUtils.isEmpty(amProtocol)) {
                            mSetupData.setIncomingProtocol(this, amProtocol);
                            final Account account = mSetupData.getAccount();
                            setDefaultsForProtocol(account);
                            mState = STATE_CREDENTIALS;
                        } else {
                            mState = STATE_TYPE;
                        }
                    }
                }
                updateContentFragment(true /* addToBackstack */);
                break;
            case STATE_TYPE:
                // We either got here through "Manual Setup" or because we didn't find the provider
                mState = STATE_CREDENTIALS;
                updateContentFragment(true /* addToBackstack */);
                break;
            case STATE_AB:
                if (possiblyDivertToGmail()) {
                    return;
                }
                mState = STATE_CREDENTIALS;
                updateContentFragment(true /* addToBackstack */);
                break;
            case STATE_CREDENTIALS:
                collectCredentials();
                if (mIsPreConfiguredProvider) {
                    mState = STATE_CHECKING_PRECONFIGURED;
                    initiateCheckSettingsFragment(SetupDataFragment.CHECK_INCOMING
                            | SetupDataFragment.CHECK_OUTGOING);
                } else {
                    populateHostAuthsFromSetupData();
                    if (mSkipAutoDiscover) {
                        mState = STATE_MANUAL_INCOMING;
                        updateContentFragment(true /* addToBackstack */);
                    } else {
                        mState = STATE_AUTO_DISCOVER;
                        initiateAutoDiscover();
                    }
                }
                break;
            case STATE_CHECKING_PRECONFIGURED:
                if (mPreConfiguredFailed) {
                    if (mPasswordFailed) {
                        // Get rid of the previous instance of the AccountSetupCredentialsFragment.
                        FragmentManager fm = getFragmentManager();
                        fm.popBackStackImmediate(CREDENTIALS_BACKSTACK_TAG, 0);
                        final AccountSetupCredentialsFragment f = (AccountSetupCredentialsFragment)
                                getContentFragment();
                        f.setPasswordFailed(mPasswordFailed);
                        resetStateFromCurrentFragment();
                    } else {
                        mState = STATE_MANUAL_INCOMING;
                        updateContentFragment(true /* addToBackstack */);
                    }
                } else {
                    mState = STATE_OPTIONS;
                    updateContentFragment(true /* addToBackstack */);
                }
                break;
            case STATE_AUTO_DISCOVER:
                // TODO: figure out if we can skip past manual setup
                mState = STATE_MANUAL_INCOMING;
                updateContentFragment(true);
                break;
            case STATE_MANUAL_INCOMING:
                onIncomingComplete();
                mState = STATE_CHECKING_INCOMING;
                initiateCheckSettingsFragment(SetupDataFragment.CHECK_INCOMING);
                break;
            case STATE_CHECKING_INCOMING:
                final EmailServiceUtils.EmailServiceInfo serviceInfo =
                        mSetupData.getIncomingServiceInfo(this);
                if (serviceInfo.usesSmtp) {
                    mState = STATE_MANUAL_OUTGOING;
                } else {
                    mState = STATE_OPTIONS;
                }
                updateContentFragment(true /* addToBackstack */);
                break;
            case STATE_MANUAL_OUTGOING:
                onOutgoingComplete();
                mState = STATE_CHECKING_OUTGOING;
                initiateCheckSettingsFragment(SetupDataFragment.CHECK_OUTGOING);
                break;
            case STATE_CHECKING_OUTGOING:
                mState = STATE_OPTIONS;
                updateContentFragment(true /* addToBackstack */);
                break;
            case STATE_OPTIONS:
                mState = STATE_CREATING;
                initiateAccountCreation();
                break;
            case STATE_CREATING:
                mState = STATE_NAMES;
                updateContentFragment(true /* addToBackstack */);
                if (mSetupData.getFlowMode() ==
                        AuthenticatorSetupIntentHelper.FLOW_MODE_FORCE_CREATE) {
                    getFragmentManager().executePendingTransactions();
                    initiateAccountFinalize();
                }
                break;
            case STATE_NAMES:
                initiateAccountFinalize();
                break;
            case STATE_FINALIZE:
                finish();
                break;
            default:
                LogUtils.wtf(LogUtils.TAG, "Unknown state %d", mState);
                break;
        }
    }

    /**
     * Check if we should divert to creating a Gmail account instead
     * @return true if we diverted
     */
    private boolean possiblyDivertToGmail() {
        // TODO: actually divert here
        final EmailServiceUtils.EmailServiceInfo info =
                mSetupData.getIncomingServiceInfo(this);
        if (TextUtils.equals(info.protocol, "gmail")) {
            final Bundle options = new Bundle(1);
            options.putBoolean("allowSkip", false);
            AccountManager.get(this).addAccount("com.google",
                    "mail" /* authTokenType */,
                    null,
                    options,
                    this, null, null);

            finish();
            return true;
        }
        return false;
    }

    /**
     * Block the back key if we are currently processing the "next" key"
     */
    @Override
    public void onBackPressed() {
        if (mIsProcessing) {
            return;
        }
        if (mState == STATE_NAMES) {
            finish();
        } else {
            super.onBackPressed();
        }
        // After super.onBackPressed() our fragment should be in place, so query the state we
        // installed it for
        resetStateFromCurrentFragment();
    }

    @Override
    public void setAccount(Account account) {
        mSetupData.setAccount(account);
    }

    @Override
    public void finish() {
        // If the account manager initiated the creation, and success was not reported,
        // then we assume that we're giving up (for any reason) - report failure.
        if (mReportAccountAuthenticatorError) {
            if (mAccountAuthenticatorResponse != null) {
                mAccountAuthenticatorResponse
                        .onError(AccountManager.ERROR_CODE_CANCELED, "canceled");
                mAccountAuthenticatorResponse = null;
            }
        }
        super.finish();
    }

    @Override
    public void onNextButton() {
        // Some states are handled without UI, block double-presses here
        if (!mIsProcessing) {
            proceed();
        }
    }

    /**
     * @return true to proceed, false to remain on the current screen
     */
    private boolean onBasicsComplete() {
        final AccountSetupBasicsFragment f = (AccountSetupBasicsFragment) getContentFragment();
        final String email = f.getEmail();

        // Reset the protocol choice in case the user has back-navigated here
        mSetupData.setIncomingProtocol(this, null);

        if (!TextUtils.equals(email, mSetupData.getEmail())) {
            // If the user changes their email address, clear the password failed state
            mPasswordFailed = false;
        }
        mSetupData.setEmail(email);

        final String[] emailParts = email.split("@");
        final String domain = emailParts[1].trim();
        mProvider = AccountSettingsUtils.findProviderForDomain(this, domain);
        if (mProvider != null) {
            mIsPreConfiguredProvider = true;
            if (mProvider.note != null) {
                final AccountSetupNoteDialogFragment dialogFragment =
                        AccountSetupNoteDialogFragment.newInstance(mProvider.note);
                dialogFragment.show(getFragmentManager(), AccountSetupNoteDialogFragment.TAG);
                return false;
            } else {
                return finishAutoSetup();
            }
        } else {
            mIsPreConfiguredProvider = false;
            final String existingAccountName =
                mExistingAccountsMap != null ? mExistingAccountsMap.get(email) : null;
            if (!TextUtils.isEmpty(existingAccountName)) {
                showDuplicateAccountDialog(existingAccountName);
                return false;
            } else {
                populateSetupData(mOwnerName, email);
                mSkipAutoDiscover = false;
                return true;
            }
        }
    }

    private void showDuplicateAccountDialog(final String existingAccountName) {
        final DuplicateAccountDialogFragment dialogFragment =
                DuplicateAccountDialogFragment.newInstance(existingAccountName);
        dialogFragment.show(getFragmentManager(), DuplicateAccountDialogFragment.TAG);
    }

    @Override
    public void onDuplicateAccountDialogDismiss() {
        resetStateFromCurrentFragment();
    }

    private boolean shouldDivertToManual() {
        final AccountSetupBasicsFragment f = (AccountSetupBasicsFragment) getContentFragment();
        return f.isManualSetup();
    }

    @Override
    public void onCredentialsComplete(Bundle results) {
        proceed();
    }

    private void collectCredentials() {
        final AccountSetupCredentialsFragment f = (AccountSetupCredentialsFragment)
                getContentFragment();
        final Bundle results = f.getCredentialResults();
        mSetupData.setCredentialResults(results);
        final Account account = mSetupData.getAccount();
        final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
        AccountSetupCredentialsFragment.populateHostAuthWithResults(this, recvAuth,
                mSetupData.getCredentialResults());
        mSetupData.setIncomingCredLoaded(true);
        final EmailServiceUtils.EmailServiceInfo info = mSetupData.getIncomingServiceInfo(this);
        if (info.usesSmtp) {
            final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
            AccountSetupCredentialsFragment.populateHostAuthWithResults(this, sendAuth,
                    mSetupData.getCredentialResults());
            mSetupData.setOutgoingCredLoaded(true);
        }
    }

    @Override
    public void onNoteDialogComplete() {
        finishAutoSetup();
        proceed();
    }

    @Override
    public void onNoteDialogCancel() {
        resetStateFromCurrentFragment();
    }

    /**
     * Finish the auto setup process, in some cases after showing a warning dialog.
     * Happens after onBasicsComplete
     * @return true to proceed, false to remain on the current screen
     */
    private boolean finishAutoSetup() {
        final String email = mSetupData.getEmail();

        try {
            mProvider.expandTemplates(email);

            final String primaryProtocol = HostAuth.getProtocolFromString(mProvider.incomingUri);
            EmailServiceUtils.EmailServiceInfo info =
                    EmailServiceUtils.getServiceInfo(this, primaryProtocol);
            // If the protocol isn't one we can use, and we're not diverting to gmail, try the alt
            if (!info.isGmailStub && !EmailServiceUtils.isServiceAvailable(this, info.protocol)) {
                LogUtils.d(LogUtils.TAG, "Protocol %s not available, using alternate",
                        info.protocol);
                mProvider.expandAlternateTemplates(email);
                final String alternateProtocol = HostAuth.getProtocolFromString(
                        mProvider.incomingUri);
                info = EmailServiceUtils.getServiceInfo(this, alternateProtocol);
            }
            final Account account = mSetupData.getAccount();
            final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
            recvAuth.setHostAuthFromString(mProvider.incomingUri);

            recvAuth.setUserName(mProvider.incomingUsername);
            recvAuth.mPort =
                    ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) ? info.portSsl : info.port;

            if (info.usesSmtp) {
                final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
                sendAuth.setHostAuthFromString(mProvider.outgoingUri);
                sendAuth.setUserName(mProvider.outgoingUsername);
            }

            // Populate the setup data, assuming that the duplicate account check will succeed
            populateSetupData(mOwnerName, email);

            final String duplicateAccountName =
                    mExistingAccountsMap != null ? mExistingAccountsMap.get(email) : null;
            if (duplicateAccountName != null) {
                showDuplicateAccountDialog(duplicateAccountName);
                return false;
            }
        } catch (URISyntaxException e) {
            mSkipAutoDiscover = false;
            mPreConfiguredFailed = true;
        }
        return true;
    }


    /**
     * Helper method to fill in some per-protocol defaults
     * @param account Account object to fill in
     */
    public void setDefaultsForProtocol(Account account) {
        final EmailServiceUtils.EmailServiceInfo info = mSetupData.getIncomingServiceInfo(this);
        if (info == null) return;
        account.mSyncInterval = info.defaultSyncInterval;
        account.mSyncLookback = info.defaultLookback;
        if (info.offerLocalDeletes) {
            account.setDeletePolicy(info.defaultLocalDeletes);
        }
    }

    /**
     * Populate SetupData's account with complete setup info, assumes that the receive auth is
     * created and its protocol is set
     */
    private void populateSetupData(String senderName, String senderEmail) {
        final Account account = mSetupData.getAccount();
        account.setSenderName(senderName);
        account.setEmailAddress(senderEmail);
        account.setDisplayName(senderEmail);
        setDefaultsForProtocol(account);
    }

    private void onIncomingComplete() {
        AccountSetupIncomingFragment f = (AccountSetupIncomingFragment) getContentFragment();
        f.collectUserInput();
    }

    private void onOutgoingComplete() {
        AccountSetupOutgoingFragment f = (AccountSetupOutgoingFragment) getContentFragment();
        f.collectUserInput();
    }

    // This callback method is only applicable to using Incoming/Outgoing fragments in settings mode
    @Override
    public void onAccountServerUIComplete(int checkMode) {}

    // This callback method is only applicable to using Incoming/Outgoing fragments in settings mode
    @Override
    public void onAccountServerSaveComplete() {}

    private void populateHostAuthsFromSetupData() {
        final String email = mSetupData.getEmail();
        final String[] emailParts = email.split("@");
        final String domain = emailParts[1];

        final Account account = mSetupData.getAccount();
        final EmailServiceUtils.EmailServiceInfo info =
                mSetupData.getIncomingServiceInfo(this);

        final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
        recvAuth.setUserName(email);
        recvAuth.setConnection(mSetupData.getIncomingProtocol(), domain,
                HostAuth.PORT_UNKNOWN, info.offerTls ? HostAuth.FLAG_TLS : HostAuth.FLAG_SSL);
        AccountSetupCredentialsFragment.populateHostAuthWithResults(this, recvAuth,
                mSetupData.getCredentialResults());
        mSetupData.setIncomingCredLoaded(true);

        if (info.usesSmtp) {
            final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
            sendAuth.setUserName(email);
            sendAuth.setConnection(HostAuth.LEGACY_SCHEME_SMTP, domain,
                    HostAuth.PORT_UNKNOWN, HostAuth.FLAG_TLS);
            AccountSetupCredentialsFragment.populateHostAuthWithResults(this, sendAuth,
                    mSetupData.getCredentialResults());
            mSetupData.setOutgoingCredLoaded(true);
        }
    }

    private void initiateAutoDiscover() {
        // Populate the setup data, assuming that the duplicate account check will succeed
        initiateCheckSettingsFragment(SetupDataFragment.CHECK_AUTODISCOVER);
    }

    private void initiateCheckSettingsFragment(int checkMode) {
        final Fragment f = AccountCheckSettingsFragment.newInstance(checkMode);
        final Fragment d = CheckSettingsProgressDialogFragment.newInstance(checkMode);
        getFragmentManager().beginTransaction()
                .add(f, AccountCheckSettingsFragment.TAG)
                .add(d, CheckSettingsProgressDialogFragment.TAG)
                .commit();
    }

    @Override
    public void onCheckSettingsProgressDialogCancel() {
        dismissCheckSettingsFragment();
        resetStateFromCurrentFragment();
    }

    private void dismissCheckSettingsFragment() {
        final Fragment f = getFragmentManager().findFragmentByTag(AccountCheckSettingsFragment.TAG);
        final Fragment d =
                getFragmentManager().findFragmentByTag(CheckSettingsProgressDialogFragment.TAG);
        getFragmentManager().beginTransaction()
                .remove(f)
                .remove(d)
                .commit();
    }

    @Override
    public void onCheckSettingsError(int reason, String message) {
        if (reason == CheckSettingsErrorDialogFragment.REASON_AUTHENTICATION_FAILED ||
                reason == CheckSettingsErrorDialogFragment.REASON_CERTIFICATE_REQUIRED) {
            // TODO: possibly split password and cert error conditions
            mPasswordFailed = true;
        }
        dismissCheckSettingsFragment();
        final DialogFragment f =
                CheckSettingsErrorDialogFragment.newInstance(reason, message);
        f.show(getFragmentManager(), CheckSettingsErrorDialogFragment.TAG);
    }

    @Override
    public void onCheckSettingsErrorDialogEditCertificate() {
        if (mState == STATE_CHECKING_PRECONFIGURED) {
            mPreConfiguredFailed = true;
            proceed();
        } else {
            resetStateFromCurrentFragment();
        }
        final AccountSetupIncomingFragment f = (AccountSetupIncomingFragment) getContentFragment();
        f.onCertificateRequested();
    }

    @Override
    public void onCheckSettingsErrorDialogEditSettings() {
        // If we're checking pre-configured, set a flag that we failed and navigate forwards to
        // incoming settings
        if (mState == STATE_CHECKING_PRECONFIGURED || mState == STATE_AUTO_DISCOVER) {
            mPreConfiguredFailed = true;
            proceed();
        } else {
            resetStateFromCurrentFragment();
        }
    }

    @Override
    public void onCheckSettingsComplete() {
        mPreConfiguredFailed = false;
        mPasswordFailed = false;
        dismissCheckSettingsFragment();
        proceed();
    }

    @Override
    public void onCheckSettingsAutoDiscoverComplete(int result) {
        dismissCheckSettingsFragment();
        proceed();
    }

    @Override
    public void onCheckSettingsSecurityRequired(String hostName) {
        dismissCheckSettingsFragment();
        final DialogFragment f = SecurityRequiredDialogFragment.newInstance(hostName);
        f.show(getFragmentManager(), SecurityRequiredDialogFragment.TAG);
    }

    @Override
    public void onSecurityRequiredDialogResult(boolean ok) {
        if (ok) {
            proceed();
        } else {
            resetStateFromCurrentFragment();
        }
    }

    @Override
    public void onChooseProtocol(String protocol) {
        mSetupData.setIncomingProtocol(this, protocol);
        final Account account = mSetupData.getAccount();
        setDefaultsForProtocol(account);
        proceed();
    }

    @Override
    public void onABProtocolDisambiguated(String chosenProtocol) {
        if (!TextUtils.equals(mSetupData.getIncomingProtocol(this), chosenProtocol)) {
            mIsPreConfiguredProvider = false;
            mSetupData.setIncomingProtocol(this, chosenProtocol);
            final Account account = mSetupData.getAccount();
            setDefaultsForProtocol(account);
        }
        proceed();
    }

    /**
     * Ths is called when the user clicks the "done" button.
     * It collects the data from the UI, updates the setup account record, and launches a fragment
     * which handles creating the account in the system and database.
     */
    private void initiateAccountCreation() {
        mIsProcessing = true;
        getContentFragment().setNextButtonEnabled(false);

        final Account account = mSetupData.getAccount();
        if (account.mHostAuthRecv == null) {
            throw new IllegalStateException("in AccountSetupOptions with null mHostAuthRecv");
        }

        final AccountSetupOptionsFragment fragment = (AccountSetupOptionsFragment)
                getContentFragment();
        if (fragment == null) {
            throw new IllegalStateException("Fragment missing!");
        }

        account.setDisplayName(account.getEmailAddress());
        int newFlags = account.getFlags() & ~(Account.FLAGS_BACKGROUND_ATTACHMENTS);
        final EmailServiceUtils.EmailServiceInfo serviceInfo =
                mSetupData.getIncomingServiceInfo(this);
        if (serviceInfo.offerAttachmentPreload && fragment.getBackgroundAttachmentsValue()) {
            newFlags |= Account.FLAGS_BACKGROUND_ATTACHMENTS;
        }
        final HostAuth hostAuth = account.getOrCreateHostAuthRecv(this);
        if (hostAuth.mProtocol.equals(getString(R.string.protocol_eas))) {
            try {
                final double protocolVersionDouble = Double.parseDouble(account.mProtocolVersion);
                if (protocolVersionDouble >= 12.0) {
                    // If the the account is EAS and the protocol version is above 12.0,
                    // we know that SmartForward is enabled and the various search flags
                    // should be enabled first.
                    // TODO: Move this into protocol specific code in the future.
                    newFlags |= Account.FLAGS_SUPPORTS_SMART_FORWARD |
                            Account.FLAGS_SUPPORTS_GLOBAL_SEARCH | Account.FLAGS_SUPPORTS_SEARCH;
                }
            } catch (NumberFormatException e) {
                LogUtils.wtf(LogUtils.TAG, e, "Exception thrown parsing the protocol version.");
            }
        }
        account.setFlags(newFlags);
        account.setSyncInterval(fragment.getCheckFrequencyValue());
        final Integer syncWindowValue = fragment.getAccountSyncWindowValue();
        if (syncWindowValue != null) {
            account.setSyncLookback(syncWindowValue);
        }

        // Finish setting up the account, and commit it to the database
        if (mSetupData.getPolicy() != null) {
            account.mFlags |= Account.FLAGS_SECURITY_HOLD;
            account.mPolicy = mSetupData.getPolicy();
        }

        // Finally, write the completed account (for the first time) and then
        // install it into the Account manager as well.  These are done off-thread.
        // The account manager will report back via the callback, which will take us to
        // the next operations.
        final boolean syncEmail = fragment.getSyncEmailValue();
        final boolean syncCalendar = serviceInfo.syncCalendar && fragment.getSyncCalendarValue();
        final boolean syncContacts = serviceInfo.syncContacts && fragment.getSyncContactsValue();
        final boolean enableNotifications = fragment.getNotifyValue();

        final Fragment f = AccountCreationFragment.newInstance(account, syncEmail, syncCalendar,
                syncContacts, enableNotifications);
        final FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.add(f, AccountCreationFragment.TAG);
        ft.commit();

        showCreateAccountDialog();
    }

    /**
     * Called by the account creation fragment after it has completed.
     * We do a small amount of work here before moving on to the next state.
     */
    @Override
    public void onAccountCreationFragmentComplete() {
        destroyAccountCreationFragment();
        // If the account manager initiated the creation, and success was not reported,
        // then we assume that we're giving up (for any reason) - report failure.
        if (mAccountAuthenticatorResponse != null) {
            final EmailServiceUtils.EmailServiceInfo info = mSetupData.getIncomingServiceInfo(this);
            final Bundle b = new Bundle(2);
            b.putString(AccountManager.KEY_ACCOUNT_NAME, mSetupData.getEmail());
            b.putString(AccountManager.KEY_ACCOUNT_TYPE, info.accountType);
            mAccountAuthenticatorResponse.onResult(b);
            mAccountAuthenticatorResponse = null;
            mReportAccountAuthenticatorError = false;
        }
        setResult(RESULT_OK);
        proceed();
    }

    @Override
    public void destroyAccountCreationFragment() {
        dismissCreateAccountDialog();

        final Fragment f = getFragmentManager().findFragmentByTag(AccountCreationFragment.TAG);
        if (f == null) {
            LogUtils.wtf(LogUtils.TAG, "Couldn't find AccountCreationFragment to destroy");
        }
        getFragmentManager().beginTransaction()
                .remove(f)
                .commit();
    }


    public static class CreateAccountDialogFragment extends DialogFragment {
        public static final String TAG = "CreateAccountDialogFragment";
        public CreateAccountDialogFragment() {}

        public static CreateAccountDialogFragment newInstance() {
            return new CreateAccountDialogFragment();
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            /// Show "Creating account..." dialog
            setCancelable(false);
            final ProgressDialog d = new ProgressDialog(getActivity());
            d.setIndeterminate(true);
            d.setMessage(getString(R.string.account_setup_creating_account_msg));
            return d;
        }
    }

    protected void showCreateAccountDialog() {
        CreateAccountDialogFragment.newInstance()
                .show(getFragmentManager(), CreateAccountDialogFragment.TAG);
    }

    protected void dismissCreateAccountDialog() {
        final DialogFragment f = (DialogFragment)
                getFragmentManager().findFragmentByTag(CreateAccountDialogFragment.TAG);
        if (f != null) {
            f.dismiss();
        }
    }

    public static class CreateAccountErrorDialogFragment extends DialogFragment
            implements DialogInterface.OnClickListener {
        public CreateAccountErrorDialogFragment() {}

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final String message = getString(R.string.account_setup_failed_dlg_auth_message,
                    R.string.system_account_create_failed);

            setCancelable(false);
            return new AlertDialog.Builder(getActivity())
                    .setIconAttribute(android.R.attr.alertDialogIcon)
                    .setTitle(R.string.account_setup_failed_dlg_title)
                    .setMessage(message)
                    .setPositiveButton(android.R.string.ok, this)
                    .create();
        }

        @Override
        public void onClick(DialogInterface dialog, int which) {
            getActivity().finish();
        }
    }

    /**
     * This is called if MailService.setupAccountManagerAccount() fails for some reason
     */
    @Override
    public void showCreateAccountErrorDialog() {
        new CreateAccountErrorDialogFragment().show(getFragmentManager(), null);
    }

    /**
     * Collect the data from AccountSetupNames and finish up account creation
     */
    private void initiateAccountFinalize() {
        mIsProcessing = true;
        getContentFragment().setNextButtonEnabled(false);

        AccountSetupNamesFragment fragment = (AccountSetupNamesFragment) getContentFragment();
        // Update account object from UI
        final Account account = mSetupData.getAccount();
        final String description = fragment.getDescription();
        if (!TextUtils.isEmpty(description)) {
            account.setDisplayName(description);
        }
        account.setSenderName(fragment.getSenderName());

        final Fragment f = AccountFinalizeFragment.newInstance(account);
        final FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.add(f, AccountFinalizeFragment.TAG);
        ft.commit();
    }

    /**
     * Called when the AccountFinalizeFragment has finished its tasks
     */
    @Override
    public void onAccountFinalizeFragmentComplete() {
        finish();
    }
}
