Split AccountSetupOptions into Activity and Fragment

Change-Id: I995c24315a26d14fbff9f1acd1bb7711403f418c
diff --git a/res/layout-sw600dp-land/account_setup_options.xml b/res/layout-sw600dp-land/account_setup_options.xml
index cf13c58..814cf07 100644
--- a/res/layout-sw600dp-land/account_setup_options.xml
+++ b/res/layout-sw600dp-land/account_setup_options.xml
@@ -21,7 +21,7 @@
 
     <RelativeLayout
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         android:paddingTop="@dimen/setup_padding_top"
         android:paddingLeft="@dimen/setup_padding_left"
         android:paddingRight="@dimen/setup_padding_right"
@@ -72,9 +72,11 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             >
-            <include
-                layout="@layout/account_setup_options_common"
-                />
+            <fragment
+                class="com.android.email.activity.setup.AccountSetupOptionsFragment"
+                android:id="@+id/options_fragment"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
         </FrameLayout>
     </RelativeLayout>
 </ScrollView>
diff --git a/res/layout-sw600dp-port/account_setup_options.xml b/res/layout-sw600dp-port/account_setup_options.xml
index b6f136d..0d53e6f 100644
--- a/res/layout-sw600dp-port/account_setup_options.xml
+++ b/res/layout-sw600dp-port/account_setup_options.xml
@@ -32,7 +32,7 @@
 
         <RelativeLayout
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
+            android:layout_height="0dp"
             android:layout_weight="1"
             >
 
@@ -63,9 +63,11 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 >
-                <include
-                    layout="@layout/account_setup_options_common"
-                    />
+                <fragment
+                    class="com.android.email.activity.setup.AccountSetupOptionsFragment"
+                    android:id="@+id/options_fragment"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
             </FrameLayout>
         </RelativeLayout>
 
diff --git a/res/layout-sw600dp/account_setup_options_common.xml b/res/layout-sw600dp/account_setup_options_fragment.xml
similarity index 100%
rename from res/layout-sw600dp/account_setup_options_common.xml
rename to res/layout-sw600dp/account_setup_options_fragment.xml
diff --git a/res/layout/account_setup_options.xml b/res/layout/account_setup_options.xml
index c4f1724..7a629c9 100644
--- a/res/layout/account_setup_options.xml
+++ b/res/layout/account_setup_options.xml
@@ -23,70 +23,16 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_weight="1"
         android:orientation="vertical"
         android:paddingTop="@dimen/setup_fragment_padding_top"
         android:paddingLeft="@dimen/setup_fragment_padding_left"
         android:paddingRight="@dimen/setup_fragment_padding_right" >
 
-        <TextView
-            android:text="@string/account_setup_options_mail_check_frequency_label"
-            android:layout_height="wrap_content"
+        <fragment
+            class="com.android.email.activity.setup.AccountSetupOptionsFragment"
+            android:id="@+id/options_fragment"
             android:layout_width="match_parent"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="?android:attr/textColorPrimary" />
-        <Spinner
-            android:id="@+id/account_check_frequency"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent" />
-        <!-- TODO quick fix - phone UX still TBD  -->
-        <LinearLayout
-            android:id="@+id/account_sync_window_row"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:visibility="gone"
-            >
-            <TextView
-                android:id="@+id/account_sync_window_label"
-                android:text="@string/account_setup_options_mail_window_label"
-                android:layout_height="wrap_content"
-                android:layout_width="match_parent"
-                android:textAppearance="?android:attr/textAppearanceSmall"
-                android:textColor="?android:attr/textColorPrimary" />
-            <Spinner
-                android:id="@+id/account_sync_window"
-                android:layout_height="wrap_content"
-                android:layout_width="match_parent" />
-        </LinearLayout>
-        <CheckBox
-            android:id="@+id/account_notify"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:text="@string/account_setup_options_notify_label" />
-        <CheckBox
-            android:id="@+id/account_sync_contacts"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:text="@string/account_setup_options_sync_contacts_label"
-            android:visibility="gone" />
-        <CheckBox
-            android:id="@+id/account_sync_calendar"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:text="@string/account_setup_options_sync_calendar_label"
-            android:visibility="gone" />
-        <CheckBox
-            android:id="@+id/account_sync_email"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:text="@string/account_setup_options_sync_email_label" />
-
-        <CheckBox
-            android:id="@+id/account_background_attachments"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:text="@string/account_setup_options_background_attachments_label" />
+            android:layout_height="wrap_content" />
 
         <include layout="@layout/account_setup_buttons" />
 
diff --git a/res/layout/account_setup_options_fragment.xml b/res/layout/account_setup_options_fragment.xml
new file mode 100644
index 0000000..bae9431
--- /dev/null
+++ b/res/layout/account_setup_options_fragment.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingTop="@dimen/setup_fragment_padding_top"
+    android:paddingLeft="@dimen/setup_fragment_padding_left"
+    android:paddingRight="@dimen/setup_fragment_padding_right" >
+    <TextView
+        android:text="@string/account_setup_options_mail_check_frequency_label"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?android:attr/textColorPrimary" />
+    <Spinner
+        android:id="@+id/account_check_frequency"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent" />
+    <!-- TODO quick fix - phone UX still TBD  -->
+    <LinearLayout
+        android:id="@+id/account_sync_window_row"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:visibility="gone" >
+        <TextView
+            android:id="@+id/account_sync_window_label"
+            android:text="@string/account_setup_options_mail_window_label"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorPrimary" />
+        <Spinner
+            android:id="@+id/account_sync_window"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent" />
+    </LinearLayout>
+    <CheckBox
+        android:id="@+id/account_notify"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:text="@string/account_setup_options_notify_label" />
+    <CheckBox
+        android:id="@+id/account_sync_contacts"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:text="@string/account_setup_options_sync_contacts_label"
+        android:visibility="gone" />
+    <CheckBox
+        android:id="@+id/account_sync_calendar"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:text="@string/account_setup_options_sync_calendar_label"
+        android:visibility="gone" />
+    <CheckBox
+        android:id="@+id/account_sync_email"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:text="@string/account_setup_options_sync_email_label" />
+
+    <CheckBox
+        android:id="@+id/account_background_attachments"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:text="@string/account_setup_options_background_attachments_label" />
+</LinearLayout>
\ No newline at end of file
diff --git a/src/com/android/email/activity/setup/AccountSetupOptions.java b/src/com/android/email/activity/setup/AccountSetupOptions.java
index 5c26ad2..96478a9 100644
--- a/src/com/android/email/activity/setup/AccountSetupOptions.java
+++ b/src/com/android/email/activity/setup/AccountSetupOptions.java
@@ -18,63 +18,50 @@
 
 import android.accounts.AccountAuthenticatorResponse;
 import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
 import android.accounts.AccountManagerFuture;
 import android.accounts.AuthenticatorException;
 import android.accounts.OperationCanceledException;
 import android.app.Activity;
 import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.app.LoaderManager;
 import android.app.ProgressDialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.os.AsyncTask;
+import android.content.Loader;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.Spinner;
 
 import com.android.email.R;
 import com.android.email.activity.ActivityHelper;
 import com.android.email.activity.UiUtilities;
 import com.android.email.service.EmailServiceUtils;
-import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
 import com.android.email2.ui.MailActivityEmail;
 import com.android.emailcommon.Logging;
 import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.Policy;
 import com.android.emailcommon.service.EmailServiceProxy;
-import com.android.emailcommon.service.SyncWindow;
-import com.android.emailcommon.utility.Utility;
 import com.android.mail.preferences.AccountPreferences;
+import com.android.mail.ui.MailAsyncTaskLoader;
 import com.android.mail.utils.LogUtils;
 
 import java.io.IOException;
 
 public class AccountSetupOptions extends AccountSetupActivity implements OnClickListener {
     private static final String EXTRA_IS_PROCESSING_KEY = "com.android.email.is_processing";
+    private static final String ACCOUNT_FINALIZE_FRAGMENT_TAG = "AccountFinalizeFragment";
 
-    private Spinner mCheckFrequencyView;
-    private Spinner mSyncWindowView;
-    private CheckBox mNotifyView;
-    private CheckBox mSyncContactsView;
-    private CheckBox mSyncCalendarView;
-    private CheckBox mSyncEmailView;
-    private CheckBox mBackgroundAttachmentsView;
-    private View mAccountSyncWindowRow;
     private boolean mDonePressed = false;
-    private EmailServiceInfo mServiceInfo;
     private boolean mIsProcessing = false;
 
-    private ProgressDialog mCreateAccountDialog;
-
     public static final int REQUEST_CODE_ACCEPT_POLICIES = 1;
 
-    /** Default sync window for new EAS accounts */
-    private static final int SYNC_WINDOW_EAS_DEFAULT = SyncWindow.SYNC_WINDOW_1_WEEK;
 
     public static void actionOptions(Activity fromActivity, SetupDataFragment setupData) {
         final Intent intent = new ForwardingIntent(fromActivity, AccountSetupOptions.class);
@@ -88,61 +75,10 @@
         ActivityHelper.debugSetWindowFlags(this);
         setContentView(R.layout.account_setup_options);
 
-        mCheckFrequencyView = UiUtilities.getView(this, R.id.account_check_frequency);
-        mSyncWindowView = UiUtilities.getView(this, R.id.account_sync_window);
-        mNotifyView = UiUtilities.getView(this, R.id.account_notify);
-        mSyncContactsView = UiUtilities.getView(this, R.id.account_sync_contacts);
-        mSyncCalendarView = UiUtilities.getView(this, R.id.account_sync_calendar);
-        mSyncEmailView = UiUtilities.getView(this, R.id.account_sync_email);
-        mSyncEmailView.setChecked(true);
-        mBackgroundAttachmentsView = UiUtilities.getView(this, R.id.account_background_attachments);
-        mBackgroundAttachmentsView.setChecked(true);
         UiUtilities.getView(this, R.id.previous).setOnClickListener(this);
         UiUtilities.getView(this, R.id.next).setOnClickListener(this);
-        mAccountSyncWindowRow = UiUtilities.getView(this, R.id.account_sync_window_row);
 
-        final Account account = mSetupData.getAccount();
-        mServiceInfo = EmailServiceUtils.getServiceInfo(getApplicationContext(),
-                account.mHostAuthRecv.mProtocol);
-        final CharSequence[] frequencyValues = mServiceInfo.syncIntervals;
-        final CharSequence[] frequencyEntries = mServiceInfo.syncIntervalStrings;
 
-        // Now create the array used by the sync interval Spinner
-        final SpinnerOption[] checkFrequencies = new SpinnerOption[frequencyEntries.length];
-        for (int i = 0; i < frequencyEntries.length; i++) {
-            checkFrequencies[i] = new SpinnerOption(
-                    Integer.valueOf(frequencyValues[i].toString()), frequencyEntries[i].toString());
-        }
-        final ArrayAdapter<SpinnerOption> checkFrequenciesAdapter =
-                new ArrayAdapter<SpinnerOption>(this, android.R.layout.simple_spinner_item,
-                        checkFrequencies);
-        checkFrequenciesAdapter
-                .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-        mCheckFrequencyView.setAdapter(checkFrequenciesAdapter);
-
-        if (mServiceInfo.offerLookback) {
-            enableLookbackSpinner();
-        }
-
-        mNotifyView.setChecked(true); // By default, we want notifications on
-        SpinnerOption.setSpinnerOptionValue(mCheckFrequencyView, account.getSyncInterval());
-
-        if (mServiceInfo.syncContacts) {
-            mSyncContactsView.setVisibility(View.VISIBLE);
-            mSyncContactsView.setChecked(true);
-            UiUtilities.setVisibilitySafe(this, R.id.account_sync_contacts_divider, View.VISIBLE);
-        }
-        if (mServiceInfo.syncCalendar) {
-            mSyncCalendarView.setVisibility(View.VISIBLE);
-            mSyncCalendarView.setChecked(true);
-            UiUtilities.setVisibilitySafe(this, R.id.account_sync_calendar_divider, View.VISIBLE);
-        }
-
-        if (!mServiceInfo.offerAttachmentPreload) {
-            mBackgroundAttachmentsView.setVisibility(View.GONE);
-            UiUtilities.setVisibilitySafe(this, R.id.account_background_attachments_divider,
-                    View.GONE);
-        }
 
         mIsProcessing = savedInstanceState != null &&
                 savedInstanceState.getBoolean(EXTRA_IS_PROCESSING_KEY, false);
@@ -201,7 +137,6 @@
      * the account to the database (making it real for the first time.)
      * Finally, we call setupAccountManagerAccount(), which will eventually complete via callback.
      */
-    @SuppressWarnings("deprecation")
     private void onDone() {
         final Account account = mSetupData.getAccount();
         if (account.isSaved()) {
@@ -212,18 +147,26 @@
             throw new IllegalStateException("in AccountSetupOptions with null mHostAuthRecv");
         }
 
+        final AccountSetupOptionsFragment fragment = (AccountSetupOptionsFragment)
+                getFragmentManager().findFragmentById(R.id.options_fragment);
+        if (fragment == null) {
+            throw new IllegalStateException("Fragment missing!");
+        }
+
         mIsProcessing = true;
         account.setDisplayName(account.getEmailAddress());
         int newFlags = account.getFlags() & ~(Account.FLAGS_BACKGROUND_ATTACHMENTS);
-        if (mServiceInfo.offerAttachmentPreload && mBackgroundAttachmentsView.isChecked()) {
+        final EmailServiceUtils.EmailServiceInfo serviceInfo =
+                EmailServiceUtils.getServiceInfo(getApplicationContext(),
+                        account.mHostAuthRecv.mProtocol);
+        if (serviceInfo.offerAttachmentPreload && fragment.getBackgroundAttachmentsValue()) {
             newFlags |= Account.FLAGS_BACKGROUND_ATTACHMENTS;
         }
         account.setFlags(newFlags);
-        account.setSyncInterval((Integer)((SpinnerOption)mCheckFrequencyView
-                .getSelectedItem()).value);
-        if (mAccountSyncWindowRow.getVisibility() == View.VISIBLE) {
-            account.setSyncLookback(
-                    (Integer)((SpinnerOption)mSyncWindowView.getSelectedItem()).value);
+        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
@@ -238,210 +181,361 @@
         // 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 email = mSyncEmailView.isChecked();
-        final boolean calendar = mServiceInfo.syncCalendar && mSyncCalendarView.isChecked();
-        final boolean contacts = mServiceInfo.syncContacts && mSyncContactsView.isChecked();
+        final Bundle args = new Bundle(5);
+        args.putParcelable(AccountFinalizeFragment.ACCOUNT_TAG, account);
+        args.putBoolean(AccountFinalizeFragment.SYNC_EMAIL_TAG, fragment.getSyncEmailValue());
+        final boolean calendar = serviceInfo.syncCalendar && fragment.getSyncCalendarValue();
+        args.putBoolean(AccountFinalizeFragment.SYNC_CALENDAR_TAG, calendar);
+        final boolean contacts = serviceInfo.syncContacts && fragment.getSyncContactsValue();
+        args.putBoolean(AccountFinalizeFragment.SYNC_CONTACTS_TAG, contacts);
+        args.putBoolean(AccountFinalizeFragment.NOTIFICATIONS_TAG, fragment.getNotifyValue());
+
+        final Fragment f = new AccountFinalizeFragment();
+        f.setArguments(args);
+        final FragmentTransaction ft = getFragmentManager().beginTransaction();
+        ft.add(f, ACCOUNT_FINALIZE_FRAGMENT_TAG);
+        ft.commit();
 
         showCreateAccountDialog();
-        Utility.runAsync(new Runnable() {
-            @Override
-            public void run() {
-                final Context context = AccountSetupOptions.this;
-                AccountSettingsUtils.commitSettings(context, account);
-                EmailServiceUtils.setupAccountManagerAccount(context, account,
-                        email, calendar, contacts, mAccountManagerCallback);
-
-                // We can move the notification setting to the inbox FolderPreferences later, once
-                // we know what the inbox is
-                final AccountPreferences accountPreferences =
-                        new AccountPreferences(context, account.getEmailAddress());
-                accountPreferences.setDefaultInboxNotificationsEnabled(mNotifyView.isChecked());
-            }
-        });
     }
 
-    private void showCreateAccountDialog() {
-        /// Show "Creating account..." dialog
-        mCreateAccountDialog = new ProgressDialog(this);
-        mCreateAccountDialog.setIndeterminate(true);
-        mCreateAccountDialog.setMessage(getString(R.string.account_setup_creating_account_msg));
-        mCreateAccountDialog.show();
+    public void destroyAccountFinalizeFragment() {
+        final Fragment f = getFragmentManager().findFragmentByTag(ACCOUNT_FINALIZE_FRAGMENT_TAG);
+        if (f == null) {
+            LogUtils.wtf(LogUtils.TAG, "Couldn't find AccountFinalizeFragment to destroy");
+        }
+        final FragmentTransaction ft = getFragmentManager().beginTransaction();
+        ft.remove(f);
+        ft.commit();
     }
 
     /**
-     * This is called at the completion of MailService.setupAccountManagerAccount()
+     * This retained headless fragment acts as a container for the multi-step task of creating the
+     * AccountManager account and saving our account object to the database, as well as some misc
+     * related background tasks.
+     *
+     * TODO: move this to a separate file, probably
      */
-    AccountManagerCallback<Bundle> mAccountManagerCallback = new AccountManagerCallback<Bundle>() {
-        @Override
-        public void run(AccountManagerFuture<Bundle> future) {
-            try {
-                // Block until the operation completes
-                future.getResult();
-                AccountSetupOptions.this.runOnUiThread(new Runnable() {
-                    @Override
-                    public void run() {
-                        optionsComplete();
-                    }
-                });
-                return;
-            } catch (OperationCanceledException e) {
-                LogUtils.d(Logging.LOG_TAG, "addAccount was canceled");
-            } catch (IOException e) {
-                LogUtils.d(Logging.LOG_TAG, "addAccount failed: " + e);
-            } catch (AuthenticatorException e) {
-                LogUtils.d(Logging.LOG_TAG, "addAccount failed: " + e);
-            }
-            showErrorDialog(R.string.account_setup_failed_dlg_auth_message,
-                    R.string.system_account_create_failed);
+    public static class AccountFinalizeFragment extends Fragment {
+        public static final String ACCOUNT_TAG = "account";
+        public static final String SYNC_EMAIL_TAG = "email";
+        public static final String SYNC_CALENDAR_TAG = "calendar";
+        public static final String SYNC_CONTACTS_TAG = "contacts";
+        public static final String NOTIFICATIONS_TAG = "notifications";
+
+        private static final String SAVESTATE_STAGE = "AccountFinalizeFragment.stage";
+        private static final int STAGE_BEFORE_ACCOUNT_SECURITY = 0;
+        private static final int STAGE_REFRESHING_ACCOUNT = 1;
+        private static final int STAGE_WAITING_FOR_ACCOUNT_SECURITY = 2;
+        private static final int STAGE_AFTER_ACCOUNT_SECURITY = 3;
+        private int mStage = 0;
+
+        private Context mAppContext;
+        private final Handler mHandler;
+
+        AccountFinalizeFragment() {
+            mHandler = new Handler();
         }
-    };
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            setRetainInstance(true);
+            mAppContext = getActivity().getApplicationContext();
+            if (savedInstanceState != null) {
+                mStage = savedInstanceState.getInt(SAVESTATE_STAGE);
+            }
+        }
+
+        @Override
+        public void onSaveInstanceState(Bundle outState) {
+            super.onSaveInstanceState(outState);
+            outState.putInt(SAVESTATE_STAGE, mStage);
+        }
+
+        @Override
+        public void onResume() {
+            super.onResume();
+
+            switch (mStage) {
+                case STAGE_BEFORE_ACCOUNT_SECURITY:
+                    kickBeforeAccountSecurityLoader();
+                    break;
+                case STAGE_REFRESHING_ACCOUNT:
+                    kickRefreshingAccountLoader();
+                    break;
+                case STAGE_WAITING_FOR_ACCOUNT_SECURITY:
+                    // TODO: figure out when we might get here and what to do if we do
+                    break;
+                case STAGE_AFTER_ACCOUNT_SECURITY:
+                    kickAfterAccountSecurityLoader();
+                    break;
+            }
+        }
+
+        private void kickBeforeAccountSecurityLoader() {
+            final LoaderManager loaderManager = getLoaderManager();
+
+            loaderManager.destroyLoader(STAGE_REFRESHING_ACCOUNT);
+            loaderManager.destroyLoader(STAGE_AFTER_ACCOUNT_SECURITY);
+            loaderManager.initLoader(STAGE_BEFORE_ACCOUNT_SECURITY, getArguments(),
+                    new BeforeAccountSecurityCallbacks());
+        }
+
+        private void kickRefreshingAccountLoader() {
+            final LoaderManager loaderManager = getLoaderManager();
+
+            loaderManager.destroyLoader(STAGE_BEFORE_ACCOUNT_SECURITY);
+            loaderManager.destroyLoader(STAGE_AFTER_ACCOUNT_SECURITY);
+            loaderManager.initLoader(STAGE_REFRESHING_ACCOUNT, getArguments(),
+                    new RefreshAccountCallbacks());
+        }
+
+        private void kickAfterAccountSecurityLoader() {
+            final LoaderManager loaderManager = getLoaderManager();
+
+            loaderManager.destroyLoader(STAGE_BEFORE_ACCOUNT_SECURITY);
+            loaderManager.destroyLoader(STAGE_REFRESHING_ACCOUNT);
+            loaderManager.initLoader(STAGE_AFTER_ACCOUNT_SECURITY, getArguments(),
+                    new AfterAccountSecurityCallbacks());
+        }
+
+        private class BeforeAccountSecurityCallbacks
+                implements LoaderManager.LoaderCallbacks<Boolean> {
+            public BeforeAccountSecurityCallbacks() {}
+
+            @Override
+            public Loader<Boolean> onCreateLoader(int id, Bundle args) {
+                final Account account = args.getParcelable(ACCOUNT_TAG);
+                final boolean email = args.getBoolean(SYNC_EMAIL_TAG);
+                final boolean calendar = args.getBoolean(SYNC_CALENDAR_TAG);
+                final boolean contacts = args.getBoolean(SYNC_CONTACTS_TAG);
+                final boolean notificationsEnabled = args.getBoolean(NOTIFICATIONS_TAG);
+
+                /**
+                 * Task loader returns true if we created the account, false if we bailed out.
+                 */
+                return new MailAsyncTaskLoader<Boolean>(mAppContext) {
+                    @Override
+                    protected void onDiscardResult(Boolean result) {}
+
+                    @Override
+                    public Boolean loadInBackground() {
+                        AccountSettingsUtils.commitSettings(mAppContext, account);
+                        final AccountManagerFuture<Bundle> future =
+                                EmailServiceUtils.setupAccountManagerAccount(mAppContext, account,
+                                email, calendar, contacts, null);
+
+                        boolean createSuccess = false;
+                        try {
+                            future.getResult();
+                            createSuccess = true;
+                        } catch (OperationCanceledException e) {
+                            LogUtils.d(Logging.LOG_TAG, "addAccount was canceled");
+                        } catch (IOException e) {
+                            LogUtils.d(Logging.LOG_TAG, "addAccount failed: " + e);
+                        } catch (AuthenticatorException e) {
+                            LogUtils.d(Logging.LOG_TAG, "addAccount failed: " + e);
+                        }
+                        if (!createSuccess) {
+                            return false;
+                        }
+                        // We can move the notification setting to the inbox FolderPreferences
+                        // later, once we know what the inbox is
+                        new AccountPreferences(mAppContext, account.getEmailAddress())
+                                .setDefaultInboxNotificationsEnabled(notificationsEnabled);
+
+                        // Now that AccountManager account creation is complete, clear the
+                        // INCOMPLETE flag
+                        account.mFlags &= ~Account.FLAGS_INCOMPLETE;
+                        AccountSettingsUtils.commitSettings(mAppContext, account);
+
+                        return true;
+                    }
+                };
+            }
+
+            @Override
+            public void onLoadFinished(Loader<Boolean> loader, Boolean success) {
+                if (success == null || !isResumed()) {
+                    return;
+                }
+                if (success) {
+                    mStage = STAGE_REFRESHING_ACCOUNT;
+                    kickRefreshingAccountLoader();
+                } else {
+                    final AccountSetupOptions activity = (AccountSetupOptions)getActivity();
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (!isResumed()) {
+                                return;
+                            }
+                            // Can't do this from within onLoadFinished
+                            activity.destroyAccountFinalizeFragment();
+                            activity.showCreateAccountErrorDialog();
+                        }
+                    });
+                }
+            }
+
+            @Override
+            public void onLoaderReset(Loader<Boolean> loader) {}
+        }
+
+        private class RefreshAccountCallbacks implements LoaderManager.LoaderCallbacks<Account> {
+
+            @Override
+            public Loader<Account> onCreateLoader(int id, Bundle args) {
+                final Account account = args.getParcelable(ACCOUNT_TAG);
+                return new MailAsyncTaskLoader<Account>(mAppContext) {
+                    @Override
+                    protected void onDiscardResult(Account result) {}
+
+                    @Override
+                    public Account loadInBackground() {
+                        account.refresh(mAppContext);
+                        return account;
+                    }
+                };
+            }
+
+            @Override
+            public void onLoadFinished(Loader<Account> loader, Account account) {
+                if (account == null || !isResumed()) {
+                    return;
+                }
+
+                getArguments().putParcelable(ACCOUNT_TAG, account);
+
+                if ((account.mFlags & Account.FLAGS_SECURITY_HOLD) != 0) {
+                    final Intent intent = AccountSecurity
+                            .actionUpdateSecurityIntent(getActivity(), account.mId, false);
+                    startActivityForResult(intent,
+                            AccountSetupOptions.REQUEST_CODE_ACCEPT_POLICIES);
+                    mStage = STAGE_WAITING_FOR_ACCOUNT_SECURITY;
+                } else {
+                    mStage = STAGE_AFTER_ACCOUNT_SECURITY;
+                    kickAfterAccountSecurityLoader();
+                }
+            }
+
+            @Override
+            public void onLoaderReset(Loader<Account> loader) {}
+        }
+
+        private class AfterAccountSecurityCallbacks
+                implements LoaderManager.LoaderCallbacks<Account> {
+            @Override
+            public Loader<Account> onCreateLoader(int id, Bundle args) {
+                final Account account = args.getParcelable(ACCOUNT_TAG);
+                return new MailAsyncTaskLoader<Account>(mAppContext) {
+                    @Override
+                    protected void onDiscardResult(Account result) {}
+
+                    @Override
+                    public Account loadInBackground() {
+                        // Clear the security hold flag now
+                        account.mFlags &= ~Account.FLAGS_SECURITY_HOLD;
+                        AccountSettingsUtils.commitSettings(mAppContext, account);
+                        // Start up services based on new account(s)
+                        MailActivityEmail.setServicesEnabledSync(mAppContext);
+                        EmailServiceUtils
+                                .startService(mAppContext, account.mHostAuthRecv.mProtocol);
+                        return account;
+                    }
+                };
+            }
+
+            @Override
+            public void onLoadFinished(Loader<Account> loader, Account account) {
+                if (account == null || !isResumed()) {
+                    return;
+                }
+
+                // Move to final setup screen
+                AccountSetupOptions activity = (AccountSetupOptions) getActivity();
+                activity.getSetupData().setAccount(account);
+                activity.proceed();
+
+                // Update the folder list (to get our starting folders, e.g. Inbox)
+                final EmailServiceProxy proxy = EmailServiceUtils.getServiceForAccount(activity,
+                        account.mId);
+                try {
+                    proxy.updateFolderList(account.mId);
+                } catch (RemoteException e) {
+                    // It's all good
+                }
+            }
+
+            @Override
+            public void onLoaderReset(Loader<Account> loader) {}
+        }
+
+        /**
+         * This is called after the AccountSecurity activity completes.
+         */
+        @Override
+        public void onActivityResult(int requestCode, int resultCode, Intent data) {
+            mStage = STAGE_AFTER_ACCOUNT_SECURITY;
+            // onResume() will be called immediately after this to kick the next loader
+        }
+    }
+
+    public static class CreateAccountDialogFragment extends DialogFragment {
+        CreateAccountDialogFragment() {}
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            /// Show "Creating account..." dialog
+            final ProgressDialog d = new ProgressDialog(getActivity());
+            d.setIndeterminate(true);
+            d.setMessage(getString(R.string.account_setup_creating_account_msg));
+            return d;
+        }
+    }
+
+    private void showCreateAccountDialog() {
+        new CreateAccountDialogFragment().show(getFragmentManager(), null);
+    }
+
+    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);
+
+            return new AlertDialog.Builder(getActivity())
+                    .setIconAttribute(android.R.attr.alertDialogIcon)
+                    .setTitle(getString(R.string.account_setup_failed_dlg_title))
+                    .setMessage(message)
+                    .setCancelable(true)
+                    .setPositiveButton(
+                            getString(R.string.account_setup_failed_dlg_edit_details_action), this)
+                    .create();
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            getActivity().finish();
+        }
+    }
 
     /**
      * This is called if MailService.setupAccountManagerAccount() fails for some reason
      */
-    private void showErrorDialog(final int msgResId, final Object... args) {
-        runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                new AlertDialog.Builder(AccountSetupOptions.this)
-                        .setIconAttribute(android.R.attr.alertDialogIcon)
-                        .setTitle(getString(R.string.account_setup_failed_dlg_title))
-                        .setMessage(getString(msgResId, args))
-                        .setCancelable(true)
-                        .setPositiveButton(
-                                getString(R.string.account_setup_failed_dlg_edit_details_action),
-                                new DialogInterface.OnClickListener() {
-                                    @Override
-                                    public void onClick(DialogInterface dialog, int which) {
-                                        finish();
-                                    }
-                                })
-                        .show();
-            }
-        });
+    private void showCreateAccountErrorDialog() {
+        new CreateAccountErrorDialogFragment().show(getFragmentManager(), null);
     }
 
     /**
-     * This is called after the account manager creates the new account.
+     * Background account creation has completed, so proceed to the next screen.
      */
-    private void optionsComplete() {
-        // If the account manager initiated the creation, report success at this point
-        final AccountAuthenticatorResponse authenticatorResponse =
-            mSetupData.getAccountAuthenticatorResponse();
-        if (authenticatorResponse != null) {
-            authenticatorResponse.onResult(null);
-            mSetupData.setAccountAuthenticatorResponse(null);
-        }
-
-        // Now that AccountManager account creation is complete, clear the INCOMPLETE flag
-        final Account account = mSetupData.getAccount();
-        account.mFlags &= ~Account.FLAGS_INCOMPLETE;
-        AccountSettingsUtils.commitSettings(AccountSetupOptions.this, account);
-
-        // If we've got policies for this account, ask the user to accept.
-        if ((account.mFlags & Account.FLAGS_SECURITY_HOLD) != 0) {
-            final Intent intent =
-                    AccountSecurity.actionUpdateSecurityIntent(this, account.mId, false);
-            startActivityForResult(intent, AccountSetupOptions.REQUEST_CODE_ACCEPT_POLICIES);
-            return;
-        }
-        saveAccountAndFinish();
-
-        // Update the folder list (to get our starting folders, e.g. Inbox)
-        final EmailServiceProxy proxy = EmailServiceUtils.getServiceForAccount(this, account.mId);
-        try {
-            proxy.updateFolderList(account.mId);
-        } catch (RemoteException e) {
-            // It's all good
-        }
-    }
-
-    /**
-     * This is called after the AccountSecurity activity completes.
-     */
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        saveAccountAndFinish();
-    }
-
-    /**
-     * These are the final cleanup steps when creating an account:
-     *  Clear incomplete & security hold flags
-     *  Update account in DB
-     *  Enable email services
-     *  Enable exchange services
-     *  Move to final setup screen
-     */
-    private void saveAccountAndFinish() {
-        AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
-            @Override
-            protected Void doInBackground(Void... params) {
-                final AccountSetupOptions context = AccountSetupOptions.this;
-                // Clear the security hold flag now
-                final Account account = mSetupData.getAccount();
-                account.mFlags &= ~Account.FLAGS_SECURITY_HOLD;
-                AccountSettingsUtils.commitSettings(context, account);
-                // Start up services based on new account(s)
-                MailActivityEmail.setServicesEnabledSync(context);
-                EmailServiceUtils.startService(context, account.mHostAuthRecv.mProtocol);
-                // Move to final setup screen
-                AccountSetupNames.actionSetNames(context, mSetupData);
-                finish();
-                return null;
-            }
-        };
-        asyncTask.execute();
-    }
-
-    /**
-     * Enable an additional spinner using the arrays normally handled by preferences
-     */
-    private void enableLookbackSpinner() {
-        // Show everything
-        mAccountSyncWindowRow.setVisibility(View.VISIBLE);
-
-        // Generate spinner entries using XML arrays used by the preferences
-        final CharSequence[] windowValues = getResources().getTextArray(
-                R.array.account_settings_mail_window_values);
-        final CharSequence[] windowEntries = getResources().getTextArray(
-                R.array.account_settings_mail_window_entries);
-
-        // Find a proper maximum for email lookback, based on policy (if we have one)
-        int maxEntry = windowEntries.length;
-        final Policy policy = mSetupData.getAccount().mPolicy;
-        if (policy != null) {
-            final int maxLookback = policy.mMaxEmailLookback;
-            if (maxLookback != 0) {
-                // Offset/Code   0      1      2      3      4        5
-                // Entries      auto, 1 day, 3 day, 1 week, 2 week, 1 month
-                // Lookback     N/A   1 day, 3 day, 1 week, 2 week, 1 month
-                // Since our test below is i < maxEntry, we must set maxEntry to maxLookback + 1
-                maxEntry = maxLookback + 1;
-            }
-        }
-
-        // Now create the array used by the Spinner
-        final SpinnerOption[] windowOptions = new SpinnerOption[maxEntry];
-        int defaultIndex = -1;
-        for (int i = 0; i < maxEntry; i++) {
-            final int value = Integer.valueOf(windowValues[i].toString());
-            windowOptions[i] = new SpinnerOption(value, windowEntries[i].toString());
-            if (value == SYNC_WINDOW_EAS_DEFAULT) {
-                defaultIndex = i;
-            }
-        }
-
-        final ArrayAdapter<SpinnerOption> windowOptionsAdapter =
-                new ArrayAdapter<SpinnerOption>(this, android.R.layout.simple_spinner_item,
-                        windowOptions);
-        windowOptionsAdapter
-                .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-        mSyncWindowView.setAdapter(windowOptionsAdapter);
-
-        SpinnerOption.setSpinnerOptionValue(mSyncWindowView,
-                mSetupData.getAccount().getSyncLookback());
-        if (defaultIndex >= 0) {
-            mSyncWindowView.setSelection(defaultIndex);
-        }
+    private void proceed() {
+        AccountSetupNames.actionSetNames(this, mSetupData);
+        finish();
     }
 }
diff --git a/src/com/android/email/activity/setup/AccountSetupOptionsFragment.java b/src/com/android/email/activity/setup/AccountSetupOptionsFragment.java
new file mode 100644
index 0000000..b89ab3f
--- /dev/null
+++ b/src/com/android/email/activity/setup/AccountSetupOptionsFragment.java
@@ -0,0 +1,206 @@
+/*
+ * 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.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.Spinner;
+
+import com.android.email.R;
+import com.android.email.activity.UiUtilities;
+import com.android.email.service.EmailServiceUtils;
+import com.android.emailcommon.provider.Account;
+import com.android.emailcommon.provider.Policy;
+import com.android.emailcommon.service.SyncWindow;
+
+public class AccountSetupOptionsFragment extends Fragment {
+    private Spinner mCheckFrequencyView;
+    private Spinner mSyncWindowView;
+    private CheckBox mNotifyView;
+    private CheckBox mSyncContactsView;
+    private CheckBox mSyncCalendarView;
+    private CheckBox mSyncEmailView;
+    private CheckBox mBackgroundAttachmentsView;
+    private View mAccountSyncWindowRow;
+
+    /** Default sync window for new EAS accounts */
+    private static final int SYNC_WINDOW_EAS_DEFAULT = SyncWindow.SYNC_WINDOW_1_WEEK;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        final View view = inflater.inflate(R.layout.account_setup_options_fragment, container,
+                false);
+
+        mCheckFrequencyView = UiUtilities.getView(view, R.id.account_check_frequency);
+        mSyncWindowView = UiUtilities.getView(view, R.id.account_sync_window);
+        mNotifyView = UiUtilities.getView(view, R.id.account_notify);
+        mNotifyView.setChecked(true);
+        mSyncContactsView = UiUtilities.getView(view, R.id.account_sync_contacts);
+        mSyncCalendarView = UiUtilities.getView(view, R.id.account_sync_calendar);
+        mSyncEmailView = UiUtilities.getView(view, R.id.account_sync_email);
+        mSyncEmailView.setChecked(true);
+        mBackgroundAttachmentsView = UiUtilities.getView(view, R.id.account_background_attachments);
+        mBackgroundAttachmentsView.setChecked(true);
+        mAccountSyncWindowRow = UiUtilities.getView(view, R.id.account_sync_window_row);
+
+        return view;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        final View view = getView();
+
+        final SetupDataFragment.SetupDataContainer container =
+                (SetupDataFragment.SetupDataContainer) getActivity();
+        final Account account = container.getSetupData().getAccount();
+
+        final EmailServiceUtils.EmailServiceInfo serviceInfo =
+                EmailServiceUtils.getServiceInfo(getActivity().getApplicationContext(),
+                account.mHostAuthRecv.mProtocol);
+
+        final CharSequence[] frequencyValues = serviceInfo.syncIntervals;
+        final CharSequence[] frequencyEntries = serviceInfo.syncIntervalStrings;
+
+        // Now create the array used by the sync interval Spinner
+        final SpinnerOption[] checkFrequencies = new SpinnerOption[frequencyEntries.length];
+        for (int i = 0; i < frequencyEntries.length; i++) {
+            checkFrequencies[i] = new SpinnerOption(
+                    Integer.valueOf(frequencyValues[i].toString()), frequencyEntries[i].toString());
+        }
+        final ArrayAdapter<SpinnerOption> checkFrequenciesAdapter =
+                new ArrayAdapter<SpinnerOption>(getActivity(), android.R.layout.simple_spinner_item,
+                        checkFrequencies);
+        checkFrequenciesAdapter
+                .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mCheckFrequencyView.setAdapter(checkFrequenciesAdapter);
+        SpinnerOption.setSpinnerOptionValue(mCheckFrequencyView, account.getSyncInterval());
+
+        if (serviceInfo.offerLookback) {
+            enableLookbackSpinner(account);
+        }
+
+        if (serviceInfo.syncContacts) {
+            mSyncContactsView.setVisibility(View.VISIBLE);
+            mSyncContactsView.setChecked(true);
+            UiUtilities.setVisibilitySafe(view, R.id.account_sync_contacts_divider, View.VISIBLE);
+        }
+        if (serviceInfo.syncCalendar) {
+            mSyncCalendarView.setVisibility(View.VISIBLE);
+            mSyncCalendarView.setChecked(true);
+            UiUtilities.setVisibilitySafe(view, R.id.account_sync_calendar_divider, View.VISIBLE);
+        }
+
+        if (!serviceInfo.offerAttachmentPreload) {
+            mBackgroundAttachmentsView.setVisibility(View.GONE);
+            UiUtilities.setVisibilitySafe(view, R.id.account_background_attachments_divider,
+                    View.GONE);
+        }
+    }
+
+    /**
+     * Enable an additional spinner using the arrays normally handled by preferences
+     */
+    private void enableLookbackSpinner(Account account) {
+        // Show everything
+        mAccountSyncWindowRow.setVisibility(View.VISIBLE);
+
+        // Generate spinner entries using XML arrays used by the preferences
+        final CharSequence[] windowValues = getResources().getTextArray(
+                R.array.account_settings_mail_window_values);
+        final CharSequence[] windowEntries = getResources().getTextArray(
+                R.array.account_settings_mail_window_entries);
+
+        // Find a proper maximum for email lookback, based on policy (if we have one)
+        int maxEntry = windowEntries.length;
+        final Policy policy = account.mPolicy;
+        if (policy != null) {
+            final int maxLookback = policy.mMaxEmailLookback;
+            if (maxLookback != 0) {
+                // Offset/Code   0      1      2      3      4        5
+                // Entries      auto, 1 day, 3 day, 1 week, 2 week, 1 month
+                // Lookback     N/A   1 day, 3 day, 1 week, 2 week, 1 month
+                // Since our test below is i < maxEntry, we must set maxEntry to maxLookback + 1
+                maxEntry = maxLookback + 1;
+            }
+        }
+
+        // Now create the array used by the Spinner
+        final SpinnerOption[] windowOptions = new SpinnerOption[maxEntry];
+        int defaultIndex = -1;
+        for (int i = 0; i < maxEntry; i++) {
+            final int value = Integer.valueOf(windowValues[i].toString());
+            windowOptions[i] = new SpinnerOption(value, windowEntries[i].toString());
+            if (value == SYNC_WINDOW_EAS_DEFAULT) {
+                defaultIndex = i;
+            }
+        }
+
+        final ArrayAdapter<SpinnerOption> windowOptionsAdapter =
+                new ArrayAdapter<SpinnerOption>(getActivity(), android.R.layout.simple_spinner_item,
+                        windowOptions);
+        windowOptionsAdapter
+                .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mSyncWindowView.setAdapter(windowOptionsAdapter);
+
+        SpinnerOption.setSpinnerOptionValue(mSyncWindowView, account.getSyncLookback());
+        if (defaultIndex >= 0) {
+            mSyncWindowView.setSelection(defaultIndex);
+        }
+    }
+
+    public boolean getBackgroundAttachmentsValue() {
+        return mBackgroundAttachmentsView.isChecked();
+    }
+
+    public Integer getCheckFrequencyValue() {
+        return (Integer)((SpinnerOption)mCheckFrequencyView.getSelectedItem()).value;
+    }
+
+    /**
+     * @return Sync window value or null if view is hidden
+     */
+    public Integer getAccountSyncWindowValue() {
+        if (mAccountSyncWindowRow.getVisibility() != View.VISIBLE) {
+            return null;
+        }
+        return (Integer)((SpinnerOption)mSyncWindowView.getSelectedItem()).value;
+    }
+
+    public boolean getSyncEmailValue() {
+        return mSyncEmailView.isChecked();
+    }
+
+    public boolean getSyncCalendarValue() {
+        return mSyncCalendarView.isChecked();
+    }
+
+    public boolean getSyncContactsValue() {
+        return mSyncContactsView.isChecked();
+    }
+
+    public boolean getNotifyValue() {
+        return mNotifyView.isChecked();
+    }
+}