Home | History | Annotate | Download | only in setup
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.email.activity.setup;
     18 
     19 import android.accounts.AccountAuthenticatorResponse;
     20 import android.accounts.AccountManager;
     21 import android.app.ActionBar;
     22 import android.app.ActivityManager;
     23 import android.app.AlertDialog;
     24 import android.app.Dialog;
     25 import android.app.DialogFragment;
     26 import android.app.Fragment;
     27 import android.app.FragmentManager;
     28 import android.app.FragmentTransaction;
     29 import android.app.LoaderManager;
     30 import android.app.ProgressDialog;
     31 import android.content.Context;
     32 import android.content.CursorLoader;
     33 import android.content.DialogInterface;
     34 import android.content.Intent;
     35 import android.content.Loader;
     36 import android.database.Cursor;
     37 import android.os.Bundle;
     38 import android.provider.ContactsContract;
     39 import android.support.annotation.NonNull;
     40 import android.text.TextUtils;
     41 import android.view.View;
     42 import android.view.inputmethod.InputMethodManager;
     43 import android.widget.Toast;
     44 
     45 import com.android.email.R;
     46 import com.android.email.setup.AuthenticatorSetupIntentHelper;
     47 import com.android.email.service.EmailServiceUtils;
     48 import com.android.emailcommon.VendorPolicyLoader;
     49 import com.android.emailcommon.provider.Account;
     50 import com.android.emailcommon.provider.HostAuth;
     51 import com.android.emailcommon.service.SyncWindow;
     52 import com.android.mail.analytics.Analytics;
     53 import com.android.mail.providers.MailAppProvider;
     54 import com.android.mail.providers.UIProvider;
     55 import com.android.mail.utils.LogUtils;
     56 
     57 import java.net.URISyntaxException;
     58 import java.util.HashMap;
     59 import java.util.Map;
     60 
     61 public class AccountSetupFinal extends AccountSetupActivity
     62         implements AccountFinalizeFragment.Callback,
     63         AccountSetupNoteDialogFragment.Callback, AccountCreationFragment.Callback,
     64         AccountCheckSettingsFragment.Callback, SecurityRequiredDialogFragment.Callback,
     65         CheckSettingsErrorDialogFragment.Callback, CheckSettingsProgressDialogFragment.Callback,
     66         AccountSetupTypeFragment.Callback, AccountSetupNamesFragment.Callback,
     67         AccountSetupOptionsFragment.Callback, AccountSetupBasicsFragment.Callback,
     68         AccountServerBaseFragment.Callback, AccountSetupCredentialsFragment.Callback,
     69         DuplicateAccountDialogFragment.Callback, AccountSetupABFragment.Callback {
     70 
     71     /**
     72      * Direct access for forcing account creation
     73      * For use by continuous automated test system (e.g. in conjunction with monkey tests)
     74      *
     75      * === Support for automated testing ==
     76      * This activity can also be launched directly via INTENT_FORCE_CREATE_ACCOUNT. This is intended
     77      * only for use by continuous test systems, and is currently only available when
     78      * {@link ActivityManager#isRunningInTestHarness()} is set.  To use this mode, you must
     79      * construct an intent which contains all necessary information to create the account.  No
     80      * connection checking is done, so the account may or may not actually work.  Here is a sample
     81      * command, for a gmail account "test_account" with a password of "test_password".
     82      *
     83      *      $ adb shell am start -a com.android.email.FORCE_CREATE_ACCOUNT \
     84      *          -e EMAIL test_account (at) gmail.com \
     85      *          -e USER "Test Account Name" \
     86      *          -e INCOMING imap+ssl+://test_account:test_password (at) imap.gmail.com \
     87      *          -e OUTGOING smtp+ssl+://test_account:test_password (at) smtp.gmail.com
     88      *
     89      * Note: For accounts that require the full email address in the login, encode the @ as %40.
     90      * Note: Exchange accounts that require device security policies cannot be created
     91      * automatically.
     92      *
     93      * For accounts that correspond to services in providers.xml you can also use the following form
     94      *
     95      *      $adb shell am start -a com.android.email.FORCE_CREATE_ACCOUNT \
     96      *          -e EMAIL test_account (at) gmail.com \
     97      *          -e PASSWORD test_password
     98      *
     99      * and the appropriate incoming/outgoing information will be filled in automatically.
    100      */
    101     private static String INTENT_FORCE_CREATE_ACCOUNT;
    102     private static final String EXTRA_CREATE_ACCOUNT_EMAIL = "EMAIL";
    103     private static final String EXTRA_CREATE_ACCOUNT_USER = "USER";
    104     private static final String EXTRA_CREATE_ACCOUNT_PASSWORD = "PASSWORD";
    105     private static final String EXTRA_CREATE_ACCOUNT_INCOMING = "INCOMING";
    106     private static final String EXTRA_CREATE_ACCOUNT_OUTGOING = "OUTGOING";
    107     private static final String EXTRA_CREATE_ACCOUNT_SYNC_LOOKBACK = "SYNC_LOOKBACK";
    108 
    109     private static final String CREATE_ACCOUNT_SYNC_ALL_VALUE = "ALL";
    110 
    111     private static final Boolean DEBUG_ALLOW_NON_TEST_HARNESS_CREATION = false;
    112 
    113     protected static final String ACTION_JUMP_TO_INCOMING = "jumpToIncoming";
    114     protected static final String ACTION_JUMP_TO_OUTGOING = "jumpToOutgoing";
    115     protected static final String ACTION_JUMP_TO_OPTIONS = "jumpToOptions";
    116 
    117     private static final String SAVESTATE_KEY_IS_PROCESSING = "AccountSetupFinal.is_processing";
    118     private static final String SAVESTATE_KEY_STATE = "AccountSetupFinal.state";
    119     private static final String SAVESTATE_KEY_PROVIDER = "AccountSetupFinal.provider";
    120     private static final String SAVESTATE_KEY_AUTHENTICATOR_RESPONSE = "AccountSetupFinal.authResp";
    121     private static final String SAVESTATE_KEY_REPORT_AUTHENTICATOR_ERROR =
    122             "AccountSetupFinal.authErr";
    123     private static final String SAVESTATE_KEY_IS_PRE_CONFIGURED = "AccountSetupFinal.preconfig";
    124     private static final String SAVESTATE_KEY_SKIP_AUTO_DISCOVER = "AccountSetupFinal.noAuto";
    125     private static final String SAVESTATE_KEY_PASSWORD_FAILED = "AccountSetupFinal.passwordFailed";
    126 
    127     private static final String CONTENT_FRAGMENT_TAG = "AccountSetupContentFragment";
    128     private static final String CREDENTIALS_BACKSTACK_TAG = "AccountSetupCredentialsFragment";
    129 
    130     // Collecting initial email and password
    131     private static final int STATE_BASICS = 0;
    132     // Show the user some interstitial message after email entry
    133     private static final int STATE_BASICS_POST = 1;
    134     // Account is not pre-configured, query user for account type
    135     private static final int STATE_TYPE = 2;
    136     // Account is pre-configured, but the user picked a different protocol
    137     private static final int STATE_AB = 3;
    138     // Collect initial password or oauth token
    139     private static final int STATE_CREDENTIALS = 4;
    140     // Account is a pre-configured account, run the checker
    141     private static final int STATE_CHECKING_PRECONFIGURED = 5;
    142     // Auto-discovering exchange account info, possibly other protocols later
    143     private static final int STATE_AUTO_DISCOVER = 6;
    144     // User is entering incoming settings
    145     private static final int STATE_MANUAL_INCOMING = 7;
    146     // We're checking incoming settings
    147     private static final int STATE_CHECKING_INCOMING = 8;
    148     // User is entering outgoing settings
    149     private static final int STATE_MANUAL_OUTGOING = 9;
    150     // We're checking outgoing settings
    151     private static final int STATE_CHECKING_OUTGOING = 10;
    152     // User is entering sync options
    153     private static final int STATE_OPTIONS = 11;
    154     // We're creating the account
    155     private static final int STATE_CREATING = 12;
    156     // User is entering account name and real name
    157     private static final int STATE_NAMES = 13;
    158     // we're finalizing the account
    159     private static final int STATE_FINALIZE = 14;
    160 
    161     private int mState = STATE_BASICS;
    162 
    163     private boolean mIsProcessing = false;
    164     private boolean mForceCreate = false;
    165     private boolean mReportAccountAuthenticatorError;
    166     private AccountAuthenticatorResponse mAccountAuthenticatorResponse;
    167     // True if this provider is found in our providers.xml, set after Basics
    168     private boolean mIsPreConfiguredProvider = false;
    169     // True if the user selected manual setup
    170     private boolean mSkipAutoDiscover = false;
    171     // True if validating the pre-configured provider failed and we want manual setup
    172     private boolean mPreConfiguredFailed = false;
    173 
    174     private VendorPolicyLoader.Provider mProvider;
    175     private boolean mPasswordFailed;
    176 
    177     private static final int OWNER_NAME_LOADER_ID = 0;
    178     private String mOwnerName;
    179 
    180     private static final int EXISTING_ACCOUNTS_LOADER_ID = 1;
    181     private Map<String, String> mExistingAccountsMap;
    182 
    183     @Override
    184     public void onCreate(Bundle savedInstanceState) {
    185         super.onCreate(savedInstanceState);
    186 
    187         final Intent intent = getIntent();
    188         final String action = intent.getAction();
    189 
    190         if (INTENT_FORCE_CREATE_ACCOUNT == null) {
    191             INTENT_FORCE_CREATE_ACCOUNT = getString(R.string.intent_force_create_email_account);
    192         }
    193 
    194         setContentView(R.layout.account_setup_activity);
    195 
    196         final ActionBar actionBar = getActionBar();
    197         if (actionBar != null) {
    198             // Hide the app icon.
    199             actionBar.setIcon(android.R.color.transparent);
    200             actionBar.setDisplayUseLogoEnabled(false);
    201         }
    202 
    203         if (savedInstanceState != null) {
    204             mIsProcessing = savedInstanceState.getBoolean(SAVESTATE_KEY_IS_PROCESSING, false);
    205             mState = savedInstanceState.getInt(SAVESTATE_KEY_STATE, STATE_OPTIONS);
    206             mProvider = (VendorPolicyLoader.Provider)
    207                     savedInstanceState.getSerializable(SAVESTATE_KEY_PROVIDER);
    208             mAccountAuthenticatorResponse =
    209                     savedInstanceState.getParcelable(SAVESTATE_KEY_AUTHENTICATOR_RESPONSE);
    210             mReportAccountAuthenticatorError =
    211                     savedInstanceState.getBoolean(SAVESTATE_KEY_REPORT_AUTHENTICATOR_ERROR);
    212             mIsPreConfiguredProvider =
    213                     savedInstanceState.getBoolean(SAVESTATE_KEY_IS_PRE_CONFIGURED);
    214             mSkipAutoDiscover = savedInstanceState.getBoolean(SAVESTATE_KEY_SKIP_AUTO_DISCOVER);
    215             mPasswordFailed = savedInstanceState.getBoolean(SAVESTATE_KEY_PASSWORD_FAILED);
    216         } else {
    217             // If we're not restoring from a previous state, we want to configure the initial screen
    218 
    219             // Set aside incoming AccountAuthenticatorResponse, if there was any
    220             mAccountAuthenticatorResponse = getIntent()
    221                     .getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
    222             if (mAccountAuthenticatorResponse != null) {
    223                 // When this Activity is called as part of account authentification flow,
    224                 // we are responsible for eventually reporting the result (success or failure) to
    225                 // the account manager.  Most exit paths represent an failed or abandoned setup,
    226                 // so the default is to report the error.  Success will be reported by the code in
    227                 // AccountSetupOptions that commits the finally created account.
    228                 mReportAccountAuthenticatorError = true;
    229             }
    230 
    231             // Initialize the SetupDataFragment
    232             if (INTENT_FORCE_CREATE_ACCOUNT.equals(action)) {
    233                 mSetupData.setFlowMode(AuthenticatorSetupIntentHelper.FLOW_MODE_FORCE_CREATE);
    234             } else {
    235                 final int intentFlowMode = intent.getIntExtra(
    236                         AuthenticatorSetupIntentHelper.EXTRA_FLOW_MODE,
    237                         AuthenticatorSetupIntentHelper.FLOW_MODE_UNSPECIFIED);
    238                 final String flowAccountType = intent.getStringExtra(
    239                         AuthenticatorSetupIntentHelper.EXTRA_FLOW_ACCOUNT_TYPE);
    240                 mSetupData.setAmProtocol(
    241                         EmailServiceUtils.getProtocolFromAccountType(this, flowAccountType));
    242                 mSetupData.setFlowMode(intentFlowMode);
    243             }
    244 
    245             mState = STATE_BASICS;
    246             // Support unit testing individual screens
    247             if (TextUtils.equals(ACTION_JUMP_TO_INCOMING, action)) {
    248                 mState = STATE_MANUAL_INCOMING;
    249             } else if (TextUtils.equals(ACTION_JUMP_TO_OUTGOING, action)) {
    250                 mState = STATE_MANUAL_OUTGOING;
    251             } else if (TextUtils.equals(ACTION_JUMP_TO_OPTIONS, action)) {
    252                 mState = STATE_OPTIONS;
    253             }
    254             updateContentFragment(false /* addToBackstack */);
    255             mPasswordFailed = false;
    256         }
    257 
    258         if (!mIsProcessing && mSetupData.getFlowMode() ==
    259                 AuthenticatorSetupIntentHelper.FLOW_MODE_FORCE_CREATE) {
    260             /**
    261              * To support continuous testing, we allow the forced creation of accounts.
    262              * This works in a manner fairly similar to automatic setup, in which the complete
    263              * server Uri's are available, except that we will also skip checking (as if both
    264              * checks were true) and all other UI.
    265              *
    266              * email: The email address for the new account
    267              * user: The user name for the new account
    268              * incoming: The URI-style string defining the incoming account
    269              * outgoing: The URI-style string defining the outgoing account
    270              */
    271             final String email = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_EMAIL);
    272             final String user = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_USER);
    273             final String password = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_PASSWORD);
    274             final String incoming = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_INCOMING);
    275             final String outgoing = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_OUTGOING);
    276             final String syncLookbackText = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_SYNC_LOOKBACK);
    277             final int syncLookback;
    278             if (TextUtils.equals(syncLookbackText, CREATE_ACCOUNT_SYNC_ALL_VALUE)) {
    279                 syncLookback = SyncWindow.SYNC_WINDOW_ALL;
    280             } else {
    281                 syncLookback = -1;
    282             }
    283             // If we've been explicitly provided with all the details to fill in the account, we
    284             // can use them
    285             final boolean explicitForm = !(TextUtils.isEmpty(user) ||
    286                     TextUtils.isEmpty(incoming) || TextUtils.isEmpty(outgoing));
    287             // If we haven't been provided the details, but we have the password, we can look up
    288             // the info from providers.xml
    289             final boolean implicitForm = !TextUtils.isEmpty(password) && !explicitForm;
    290             if (TextUtils.isEmpty(email) || !(explicitForm || implicitForm)) {
    291                 LogUtils.e(LogUtils.TAG, "Force account create requires extras EMAIL, " +
    292                         "USER, INCOMING, OUTGOING, or EMAIL and PASSWORD");
    293                 finish();
    294                 return;
    295             }
    296 
    297             if (implicitForm) {
    298                 final String[] emailParts = email.split("@");
    299                 final String domain = emailParts[1].trim();
    300                 mProvider = AccountSettingsUtils.findProviderForDomain(this, domain);
    301                 if (mProvider == null) {
    302                     LogUtils.e(LogUtils.TAG, "findProviderForDomain couldn't find provider");
    303                     finish();
    304                     return;
    305                 }
    306                 mIsPreConfiguredProvider = true;
    307                 mSetupData.setEmail(email);
    308                 boolean autoSetupCompleted = finishAutoSetup();
    309                 if (!autoSetupCompleted) {
    310                     LogUtils.e(LogUtils.TAG, "Force create account failed to create account");
    311                     finish();
    312                     return;
    313                 }
    314                 final Account account = mSetupData.getAccount();
    315                 final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
    316                 recvAuth.mPassword = password;
    317                 final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
    318                 sendAuth.mPassword = password;
    319             } else {
    320                 final Account account = mSetupData.getAccount();
    321 
    322                 try {
    323                     final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
    324                     recvAuth.setHostAuthFromString(incoming);
    325 
    326                     final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
    327                     sendAuth.setHostAuthFromString(outgoing);
    328                 } catch (URISyntaxException e) {
    329                     // If we can't set up the URL, don't continue
    330                     Toast.makeText(this, R.string.account_setup_username_password_toast,
    331                             Toast.LENGTH_LONG)
    332                             .show();
    333                     finish();
    334                     return;
    335                 }
    336 
    337                 populateSetupData(user, email);
    338                 // We need to do this after calling populateSetupData(), because that will
    339                 // overwrite it with the default values.
    340                 if (syncLookback >= SyncWindow.SYNC_WINDOW_ACCOUNT &&
    341                     syncLookback <= SyncWindow.SYNC_WINDOW_ALL) {
    342                     account.mSyncLookback = syncLookback;
    343                 }
    344             }
    345 
    346             mState = STATE_OPTIONS;
    347             updateContentFragment(false /* addToBackstack */);
    348             getFragmentManager().executePendingTransactions();
    349 
    350             if (!DEBUG_ALLOW_NON_TEST_HARNESS_CREATION &&
    351                     !ActivityManager.isRunningInTestHarness()) {
    352                 LogUtils.e(LogUtils.TAG,
    353                         "ERROR: Force account create only allowed while in test harness");
    354                 finish();
    355                 return;
    356             }
    357 
    358             mForceCreate = true;
    359         }
    360 
    361         // Launch a loader to look up the owner name.  It should be ready well in advance of
    362         // the time the user clicks next or manual.
    363         getLoaderManager().initLoader(OWNER_NAME_LOADER_ID, null, new OwnerNameLoaderCallbacks());
    364 
    365         // Launch a loader to cache some info about existing accounts so we can dupe-check against
    366         // them.
    367         getLoaderManager().initLoader(EXISTING_ACCOUNTS_LOADER_ID, null,
    368                 new ExistingAccountsLoaderCallbacks());
    369     }
    370 
    371     private class OwnerNameLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
    372         @Override
    373         public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
    374             return new CursorLoader(AccountSetupFinal.this,
    375                     ContactsContract.Profile.CONTENT_URI,
    376                     new String[] {ContactsContract.Profile.DISPLAY_NAME_PRIMARY},
    377                     null, null, null);
    378         }
    379 
    380         @Override
    381         public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) {
    382             if (data != null && data.moveToFirst()) {
    383                 mOwnerName = data.getString(data.getColumnIndex(
    384                         ContactsContract.Profile.DISPLAY_NAME_PRIMARY));
    385             }
    386         }
    387 
    388         @Override
    389         public void onLoaderReset(final Loader<Cursor> loader) {}
    390     }
    391 
    392     private class ExistingAccountsLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
    393         @Override
    394         public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
    395             return new CursorLoader(AccountSetupFinal.this, MailAppProvider.getAccountsUri(),
    396                     new String[] {UIProvider.AccountColumns.ACCOUNT_MANAGER_NAME,
    397                             UIProvider.AccountColumns.NAME},
    398                     null, null, null);
    399         }
    400 
    401         @Override
    402         public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) {
    403             if (data == null || !data.moveToFirst()) {
    404                 mExistingAccountsMap = null;
    405                 return;
    406             }
    407 
    408             mExistingAccountsMap = new HashMap<>();
    409 
    410             final int emailColumnIndex = data.getColumnIndex(
    411                     UIProvider.AccountColumns.ACCOUNT_MANAGER_NAME);
    412             final int displayNameColumnIndex =
    413                     data.getColumnIndex(UIProvider.AccountColumns.NAME);
    414 
    415             do {
    416                 final String email = data.getString(emailColumnIndex);
    417                 final String displayName = data.getString(displayNameColumnIndex);
    418                 mExistingAccountsMap.put(email,
    419                         TextUtils.isEmpty(displayName) ? email : displayName);
    420             } while (data.moveToNext());
    421         }
    422 
    423         @Override
    424         public void onLoaderReset(final Loader<Cursor> loader) {
    425             mExistingAccountsMap = null;
    426         }
    427     }
    428 
    429     @Override
    430     protected void onStart() {
    431         super.onStart();
    432 
    433         Analytics.getInstance().activityStart(this);
    434     }
    435 
    436     @Override
    437     protected void onResume() {
    438         super.onResume();
    439         if (mForceCreate) {
    440             mForceCreate = false;
    441 
    442             // We need to do this after onCreate so that we can ensure that the fragment is
    443             // fully created before querying it.
    444             // This will call initiateAccountCreation() for us
    445             proceed();
    446         }
    447     }
    448 
    449     @Override
    450     public void onSaveInstanceState(@NonNull Bundle outState) {
    451         super.onSaveInstanceState(outState);
    452         outState.putBoolean(SAVESTATE_KEY_IS_PROCESSING, mIsProcessing);
    453         outState.putInt(SAVESTATE_KEY_STATE, mState);
    454         outState.putSerializable(SAVESTATE_KEY_PROVIDER, mProvider);
    455         outState.putParcelable(SAVESTATE_KEY_AUTHENTICATOR_RESPONSE, mAccountAuthenticatorResponse);
    456         outState.putBoolean(SAVESTATE_KEY_REPORT_AUTHENTICATOR_ERROR,
    457                 mReportAccountAuthenticatorError);
    458         outState.putBoolean(SAVESTATE_KEY_IS_PRE_CONFIGURED, mIsPreConfiguredProvider);
    459         outState.putBoolean(SAVESTATE_KEY_PASSWORD_FAILED, mPasswordFailed);
    460     }
    461 
    462     @Override
    463     protected void onStop() {
    464         super.onStop();
    465 
    466         Analytics.getInstance().activityStop(this);
    467     }
    468 
    469     /**
    470      * Swap in the new fragment according to mState. This pushes the current fragment onto the back
    471      * stack, so only call it once per transition.
    472      */
    473     private void updateContentFragment(boolean addToBackstack) {
    474         final AccountSetupFragment f;
    475         String backstackTag = null;
    476 
    477         switch (mState) {
    478             case STATE_BASICS:
    479                 f = AccountSetupBasicsFragment.newInstance();
    480                 break;
    481             case STATE_TYPE:
    482                 f = AccountSetupTypeFragment.newInstance();
    483                 break;
    484             case STATE_AB:
    485                 f = AccountSetupABFragment.newInstance(mSetupData.getEmail(),
    486                         mSetupData.getAmProtocol(), mSetupData.getIncomingProtocol(this));
    487                 break;
    488             case STATE_CREDENTIALS:
    489                 f = AccountSetupCredentialsFragment.newInstance(mSetupData.getEmail(),
    490                         mSetupData.getIncomingProtocol(this), mSetupData.getClientCert(this),
    491                         mPasswordFailed, false /* standalone */);
    492                 backstackTag = CREDENTIALS_BACKSTACK_TAG;
    493                 break;
    494             case STATE_MANUAL_INCOMING:
    495                 f = AccountSetupIncomingFragment.newInstance(false);
    496                 break;
    497             case STATE_MANUAL_OUTGOING:
    498                 f = AccountSetupOutgoingFragment.newInstance(false);
    499                 break;
    500             case STATE_OPTIONS:
    501                 f = AccountSetupOptionsFragment.newInstance();
    502                 break;
    503             case STATE_NAMES:
    504                 f = AccountSetupNamesFragment.newInstance();
    505                 break;
    506             default:
    507                 throw new IllegalStateException("Incorrect state " + mState);
    508         }
    509         f.setState(mState);
    510         final FragmentTransaction ft = getFragmentManager().beginTransaction();
    511         ft.setCustomAnimations(R.anim.fade_in, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out);
    512         ft.replace(R.id.setup_fragment_container, f, CONTENT_FRAGMENT_TAG);
    513         if (addToBackstack) {
    514             ft.addToBackStack(backstackTag);
    515         }
    516         ft.commit();
    517 
    518         final InputMethodManager imm =
    519                 (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
    520         final View fragment_container = findViewById(R.id.setup_fragment_container);
    521         imm.hideSoftInputFromWindow(fragment_container.getWindowToken(),
    522                 0 /* flags: always hide */);
    523     }
    524 
    525     /**
    526      * Retrieve the current content fragment
    527      * @return The content fragment or null if it wasn't found for some reason
    528      */
    529     private AccountSetupFragment getContentFragment() {
    530         return (AccountSetupFragment) getFragmentManager().findFragmentByTag(CONTENT_FRAGMENT_TAG);
    531     }
    532 
    533     /**
    534      * Reads the flow state saved into the current fragment and restores mState to it, also
    535      * resetting the headline at the same time.
    536      */
    537     private void resetStateFromCurrentFragment() {
    538         AccountSetupFragment f = getContentFragment();
    539         mState = f.getState();
    540     }
    541 
    542     /**
    543      * Main choreography function to handle moving forward through scenes. Moving back should be
    544      * generally handled for us by the back stack
    545      */
    546     protected void proceed() {
    547         mIsProcessing = false;
    548         final AccountSetupFragment oldContentFragment = getContentFragment();
    549         if (oldContentFragment != null) {
    550             oldContentFragment.setNextButtonEnabled(true);
    551         }
    552 
    553         getFragmentManager().executePendingTransactions();
    554 
    555         switch (mState) {
    556             case STATE_BASICS:
    557                 final boolean advance = onBasicsComplete();
    558                 if (!advance) {
    559                     mState = STATE_BASICS_POST;
    560                     break;
    561                 } // else fall through
    562             case STATE_BASICS_POST:
    563                 if (shouldDivertToManual()) {
    564                     mSkipAutoDiscover = true;
    565                     mIsPreConfiguredProvider = false;
    566                     mState = STATE_TYPE;
    567                 } else {
    568                     mSkipAutoDiscover = false;
    569                     if (mIsPreConfiguredProvider) {
    570                         if (!TextUtils.isEmpty(mSetupData.getAmProtocol()) &&
    571                                 !TextUtils.equals(mSetupData.getAmProtocol(),
    572                                         mSetupData.getIncomingProtocol(this))) {
    573                             mState = STATE_AB;
    574                         } else {
    575                             mState = STATE_CREDENTIALS;
    576                             if (possiblyDivertToGmail()) {
    577                                 return;
    578                             }
    579                         }
    580                     } else {
    581                         final String amProtocol = mSetupData.getAmProtocol();
    582                         if (!TextUtils.isEmpty(amProtocol)) {
    583                             mSetupData.setIncomingProtocol(this, amProtocol);
    584                             final Account account = mSetupData.getAccount();
    585                             setDefaultsForProtocol(account);
    586                             mState = STATE_CREDENTIALS;
    587                         } else {
    588                             mState = STATE_TYPE;
    589                         }
    590                     }
    591                 }
    592                 updateContentFragment(true /* addToBackstack */);
    593                 break;
    594             case STATE_TYPE:
    595                 // We either got here through "Manual Setup" or because we didn't find the provider
    596                 mState = STATE_CREDENTIALS;
    597                 updateContentFragment(true /* addToBackstack */);
    598                 break;
    599             case STATE_AB:
    600                 if (possiblyDivertToGmail()) {
    601                     return;
    602                 }
    603                 mState = STATE_CREDENTIALS;
    604                 updateContentFragment(true /* addToBackstack */);
    605                 break;
    606             case STATE_CREDENTIALS:
    607                 collectCredentials();
    608                 if (mIsPreConfiguredProvider) {
    609                     mState = STATE_CHECKING_PRECONFIGURED;
    610                     initiateCheckSettingsFragment(SetupDataFragment.CHECK_INCOMING
    611                             | SetupDataFragment.CHECK_OUTGOING);
    612                 } else {
    613                     populateHostAuthsFromSetupData();
    614                     if (mSkipAutoDiscover) {
    615                         mState = STATE_MANUAL_INCOMING;
    616                         updateContentFragment(true /* addToBackstack */);
    617                     } else {
    618                         mState = STATE_AUTO_DISCOVER;
    619                         initiateAutoDiscover();
    620                     }
    621                 }
    622                 break;
    623             case STATE_CHECKING_PRECONFIGURED:
    624                 if (mPreConfiguredFailed) {
    625                     if (mPasswordFailed) {
    626                         // Get rid of the previous instance of the AccountSetupCredentialsFragment.
    627                         FragmentManager fm = getFragmentManager();
    628                         fm.popBackStackImmediate(CREDENTIALS_BACKSTACK_TAG, 0);
    629                         final AccountSetupCredentialsFragment f = (AccountSetupCredentialsFragment)
    630                                 getContentFragment();
    631                         f.setPasswordFailed(mPasswordFailed);
    632                         resetStateFromCurrentFragment();
    633                     } else {
    634                         mState = STATE_MANUAL_INCOMING;
    635                         updateContentFragment(true /* addToBackstack */);
    636                     }
    637                 } else {
    638                     mState = STATE_OPTIONS;
    639                     updateContentFragment(true /* addToBackstack */);
    640                 }
    641                 break;
    642             case STATE_AUTO_DISCOVER:
    643                 // TODO: figure out if we can skip past manual setup
    644                 mState = STATE_MANUAL_INCOMING;
    645                 updateContentFragment(true);
    646                 break;
    647             case STATE_MANUAL_INCOMING:
    648                 onIncomingComplete();
    649                 mState = STATE_CHECKING_INCOMING;
    650                 initiateCheckSettingsFragment(SetupDataFragment.CHECK_INCOMING);
    651                 break;
    652             case STATE_CHECKING_INCOMING:
    653                 final EmailServiceUtils.EmailServiceInfo serviceInfo =
    654                         mSetupData.getIncomingServiceInfo(this);
    655                 if (serviceInfo.usesSmtp) {
    656                     mState = STATE_MANUAL_OUTGOING;
    657                 } else {
    658                     mState = STATE_OPTIONS;
    659                 }
    660                 updateContentFragment(true /* addToBackstack */);
    661                 break;
    662             case STATE_MANUAL_OUTGOING:
    663                 onOutgoingComplete();
    664                 mState = STATE_CHECKING_OUTGOING;
    665                 initiateCheckSettingsFragment(SetupDataFragment.CHECK_OUTGOING);
    666                 break;
    667             case STATE_CHECKING_OUTGOING:
    668                 mState = STATE_OPTIONS;
    669                 updateContentFragment(true /* addToBackstack */);
    670                 break;
    671             case STATE_OPTIONS:
    672                 mState = STATE_CREATING;
    673                 initiateAccountCreation();
    674                 break;
    675             case STATE_CREATING:
    676                 mState = STATE_NAMES;
    677                 updateContentFragment(true /* addToBackstack */);
    678                 if (mSetupData.getFlowMode() ==
    679                         AuthenticatorSetupIntentHelper.FLOW_MODE_FORCE_CREATE) {
    680                     getFragmentManager().executePendingTransactions();
    681                     initiateAccountFinalize();
    682                 }
    683                 break;
    684             case STATE_NAMES:
    685                 initiateAccountFinalize();
    686                 break;
    687             case STATE_FINALIZE:
    688                 finish();
    689                 break;
    690             default:
    691                 LogUtils.wtf(LogUtils.TAG, "Unknown state %d", mState);
    692                 break;
    693         }
    694     }
    695 
    696     /**
    697      * Check if we should divert to creating a Gmail account instead
    698      * @return true if we diverted
    699      */
    700     private boolean possiblyDivertToGmail() {
    701         // TODO: actually divert here
    702         final EmailServiceUtils.EmailServiceInfo info =
    703                 mSetupData.getIncomingServiceInfo(this);
    704         if (TextUtils.equals(info.protocol, "gmail")) {
    705             final Bundle options = new Bundle(1);
    706             options.putBoolean("allowSkip", false);
    707             AccountManager.get(this).addAccount("com.google",
    708                     "mail" /* authTokenType */,
    709                     null,
    710                     options,
    711                     this, null, null);
    712 
    713             finish();
    714             return true;
    715         }
    716         return false;
    717     }
    718 
    719     /**
    720      * Block the back key if we are currently processing the "next" key"
    721      */
    722     @Override
    723     public void onBackPressed() {
    724         if (mIsProcessing) {
    725             return;
    726         }
    727         if (mState == STATE_NAMES) {
    728             finish();
    729         } else {
    730             super.onBackPressed();
    731         }
    732         // After super.onBackPressed() our fragment should be in place, so query the state we
    733         // installed it for
    734         resetStateFromCurrentFragment();
    735     }
    736 
    737     @Override
    738     public void setAccount(Account account) {
    739         mSetupData.setAccount(account);
    740     }
    741 
    742     @Override
    743     public void finish() {
    744         // If the account manager initiated the creation, and success was not reported,
    745         // then we assume that we're giving up (for any reason) - report failure.
    746         if (mReportAccountAuthenticatorError) {
    747             if (mAccountAuthenticatorResponse != null) {
    748                 mAccountAuthenticatorResponse
    749                         .onError(AccountManager.ERROR_CODE_CANCELED, "canceled");
    750                 mAccountAuthenticatorResponse = null;
    751             }
    752         }
    753         super.finish();
    754     }
    755 
    756     @Override
    757     public void onNextButton() {
    758         // Some states are handled without UI, block double-presses here
    759         if (!mIsProcessing) {
    760             proceed();
    761         }
    762     }
    763 
    764     /**
    765      * @return true to proceed, false to remain on the current screen
    766      */
    767     private boolean onBasicsComplete() {
    768         final AccountSetupBasicsFragment f = (AccountSetupBasicsFragment) getContentFragment();
    769         final String email = f.getEmail();
    770 
    771         // Reset the protocol choice in case the user has back-navigated here
    772         mSetupData.setIncomingProtocol(this, null);
    773 
    774         if (!TextUtils.equals(email, mSetupData.getEmail())) {
    775             // If the user changes their email address, clear the password failed state
    776             mPasswordFailed = false;
    777         }
    778         mSetupData.setEmail(email);
    779 
    780         final String[] emailParts = email.split("@");
    781         final String domain = emailParts[1].trim();
    782         mProvider = AccountSettingsUtils.findProviderForDomain(this, domain);
    783         if (mProvider != null) {
    784             mIsPreConfiguredProvider = true;
    785             if (mProvider.note != null) {
    786                 final AccountSetupNoteDialogFragment dialogFragment =
    787                         AccountSetupNoteDialogFragment.newInstance(mProvider.note);
    788                 dialogFragment.show(getFragmentManager(), AccountSetupNoteDialogFragment.TAG);
    789                 return false;
    790             } else {
    791                 return finishAutoSetup();
    792             }
    793         } else {
    794             mIsPreConfiguredProvider = false;
    795             final String existingAccountName =
    796                 mExistingAccountsMap != null ? mExistingAccountsMap.get(email) : null;
    797             if (!TextUtils.isEmpty(existingAccountName)) {
    798                 showDuplicateAccountDialog(existingAccountName);
    799                 return false;
    800             } else {
    801                 populateSetupData(mOwnerName, email);
    802                 mSkipAutoDiscover = false;
    803                 return true;
    804             }
    805         }
    806     }
    807 
    808     private void showDuplicateAccountDialog(final String existingAccountName) {
    809         final DuplicateAccountDialogFragment dialogFragment =
    810                 DuplicateAccountDialogFragment.newInstance(existingAccountName);
    811         dialogFragment.show(getFragmentManager(), DuplicateAccountDialogFragment.TAG);
    812     }
    813 
    814     @Override
    815     public void onDuplicateAccountDialogDismiss() {
    816         resetStateFromCurrentFragment();
    817     }
    818 
    819     private boolean shouldDivertToManual() {
    820         final AccountSetupBasicsFragment f = (AccountSetupBasicsFragment) getContentFragment();
    821         return f.isManualSetup();
    822     }
    823 
    824     @Override
    825     public void onCredentialsComplete(Bundle results) {
    826         proceed();
    827     }
    828 
    829     private void collectCredentials() {
    830         final AccountSetupCredentialsFragment f = (AccountSetupCredentialsFragment)
    831                 getContentFragment();
    832         final Bundle results = f.getCredentialResults();
    833         mSetupData.setCredentialResults(results);
    834         final Account account = mSetupData.getAccount();
    835         final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
    836         AccountSetupCredentialsFragment.populateHostAuthWithResults(this, recvAuth,
    837                 mSetupData.getCredentialResults());
    838         mSetupData.setIncomingCredLoaded(true);
    839         final EmailServiceUtils.EmailServiceInfo info = mSetupData.getIncomingServiceInfo(this);
    840         if (info.usesSmtp) {
    841             final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
    842             AccountSetupCredentialsFragment.populateHostAuthWithResults(this, sendAuth,
    843                     mSetupData.getCredentialResults());
    844             mSetupData.setOutgoingCredLoaded(true);
    845         }
    846     }
    847 
    848     @Override
    849     public void onNoteDialogComplete() {
    850         finishAutoSetup();
    851         proceed();
    852     }
    853 
    854     @Override
    855     public void onNoteDialogCancel() {
    856         resetStateFromCurrentFragment();
    857     }
    858 
    859     /**
    860      * Finish the auto setup process, in some cases after showing a warning dialog.
    861      * Happens after onBasicsComplete
    862      * @return true to proceed, false to remain on the current screen
    863      */
    864     private boolean finishAutoSetup() {
    865         final String email = mSetupData.getEmail();
    866 
    867         try {
    868             mProvider.expandTemplates(email);
    869 
    870             final String primaryProtocol = HostAuth.getProtocolFromString(mProvider.incomingUri);
    871             EmailServiceUtils.EmailServiceInfo info =
    872                     EmailServiceUtils.getServiceInfo(this, primaryProtocol);
    873             // If the protocol isn't one we can use, and we're not diverting to gmail, try the alt
    874             if (!info.isGmailStub && !EmailServiceUtils.isServiceAvailable(this, info.protocol)) {
    875                 LogUtils.d(LogUtils.TAG, "Protocol %s not available, using alternate",
    876                         info.protocol);
    877                 mProvider.expandAlternateTemplates(email);
    878                 final String alternateProtocol = HostAuth.getProtocolFromString(
    879                         mProvider.incomingUri);
    880                 info = EmailServiceUtils.getServiceInfo(this, alternateProtocol);
    881             }
    882             final Account account = mSetupData.getAccount();
    883             final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
    884             recvAuth.setHostAuthFromString(mProvider.incomingUri);
    885 
    886             recvAuth.setUserName(mProvider.incomingUsername);
    887             recvAuth.mPort =
    888                     ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) ? info.portSsl : info.port;
    889 
    890             if (info.usesSmtp) {
    891                 final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
    892                 sendAuth.setHostAuthFromString(mProvider.outgoingUri);
    893                 sendAuth.setUserName(mProvider.outgoingUsername);
    894             }
    895 
    896             // Populate the setup data, assuming that the duplicate account check will succeed
    897             populateSetupData(mOwnerName, email);
    898 
    899             final String duplicateAccountName =
    900                     mExistingAccountsMap != null ? mExistingAccountsMap.get(email) : null;
    901             if (duplicateAccountName != null) {
    902                 showDuplicateAccountDialog(duplicateAccountName);
    903                 return false;
    904             }
    905         } catch (URISyntaxException e) {
    906             mSkipAutoDiscover = false;
    907             mPreConfiguredFailed = true;
    908         }
    909         return true;
    910     }
    911 
    912 
    913     /**
    914      * Helper method to fill in some per-protocol defaults
    915      * @param account Account object to fill in
    916      */
    917     public void setDefaultsForProtocol(Account account) {
    918         final EmailServiceUtils.EmailServiceInfo info = mSetupData.getIncomingServiceInfo(this);
    919         if (info == null) return;
    920         account.mSyncInterval = info.defaultSyncInterval;
    921         account.mSyncLookback = info.defaultLookback;
    922         if (info.offerLocalDeletes) {
    923             account.setDeletePolicy(info.defaultLocalDeletes);
    924         }
    925     }
    926 
    927     /**
    928      * Populate SetupData's account with complete setup info, assumes that the receive auth is
    929      * created and its protocol is set
    930      */
    931     private void populateSetupData(String senderName, String senderEmail) {
    932         final Account account = mSetupData.getAccount();
    933         account.setSenderName(senderName);
    934         account.setEmailAddress(senderEmail);
    935         account.setDisplayName(senderEmail);
    936         setDefaultsForProtocol(account);
    937     }
    938 
    939     private void onIncomingComplete() {
    940         AccountSetupIncomingFragment f = (AccountSetupIncomingFragment) getContentFragment();
    941         f.collectUserInput();
    942     }
    943 
    944     private void onOutgoingComplete() {
    945         AccountSetupOutgoingFragment f = (AccountSetupOutgoingFragment) getContentFragment();
    946         f.collectUserInput();
    947     }
    948 
    949     // This callback method is only applicable to using Incoming/Outgoing fragments in settings mode
    950     @Override
    951     public void onAccountServerUIComplete(int checkMode) {}
    952 
    953     // This callback method is only applicable to using Incoming/Outgoing fragments in settings mode
    954     @Override
    955     public void onAccountServerSaveComplete() {}
    956 
    957     private void populateHostAuthsFromSetupData() {
    958         final String email = mSetupData.getEmail();
    959         final String[] emailParts = email.split("@");
    960         final String domain = emailParts[1];
    961 
    962         final Account account = mSetupData.getAccount();
    963         final EmailServiceUtils.EmailServiceInfo info =
    964                 mSetupData.getIncomingServiceInfo(this);
    965 
    966         final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
    967         recvAuth.setUserName(email);
    968         recvAuth.setConnection(mSetupData.getIncomingProtocol(), domain,
    969                 HostAuth.PORT_UNKNOWN, info.offerTls ? HostAuth.FLAG_TLS : HostAuth.FLAG_SSL);
    970         AccountSetupCredentialsFragment.populateHostAuthWithResults(this, recvAuth,
    971                 mSetupData.getCredentialResults());
    972         mSetupData.setIncomingCredLoaded(true);
    973 
    974         if (info.usesSmtp) {
    975             final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
    976             sendAuth.setUserName(email);
    977             sendAuth.setConnection(HostAuth.LEGACY_SCHEME_SMTP, domain,
    978                     HostAuth.PORT_UNKNOWN, HostAuth.FLAG_TLS);
    979             AccountSetupCredentialsFragment.populateHostAuthWithResults(this, sendAuth,
    980                     mSetupData.getCredentialResults());
    981             mSetupData.setOutgoingCredLoaded(true);
    982         }
    983     }
    984 
    985     private void initiateAutoDiscover() {
    986         // Populate the setup data, assuming that the duplicate account check will succeed
    987         initiateCheckSettingsFragment(SetupDataFragment.CHECK_AUTODISCOVER);
    988     }
    989 
    990     private void initiateCheckSettingsFragment(int checkMode) {
    991         final Fragment f = AccountCheckSettingsFragment.newInstance(checkMode);
    992         final Fragment d = CheckSettingsProgressDialogFragment.newInstance(checkMode);
    993         getFragmentManager().beginTransaction()
    994                 .add(f, AccountCheckSettingsFragment.TAG)
    995                 .add(d, CheckSettingsProgressDialogFragment.TAG)
    996                 .commit();
    997     }
    998 
    999     @Override
   1000     public void onCheckSettingsProgressDialogCancel() {
   1001         dismissCheckSettingsFragment();
   1002         resetStateFromCurrentFragment();
   1003     }
   1004 
   1005     private void dismissCheckSettingsFragment() {
   1006         final Fragment f = getFragmentManager().findFragmentByTag(AccountCheckSettingsFragment.TAG);
   1007         final Fragment d =
   1008                 getFragmentManager().findFragmentByTag(CheckSettingsProgressDialogFragment.TAG);
   1009         getFragmentManager().beginTransaction()
   1010                 .remove(f)
   1011                 .remove(d)
   1012                 .commit();
   1013     }
   1014 
   1015     @Override
   1016     public void onCheckSettingsError(int reason, String message) {
   1017         if (reason == CheckSettingsErrorDialogFragment.REASON_AUTHENTICATION_FAILED ||
   1018                 reason == CheckSettingsErrorDialogFragment.REASON_CERTIFICATE_REQUIRED) {
   1019             // TODO: possibly split password and cert error conditions
   1020             mPasswordFailed = true;
   1021         }
   1022         dismissCheckSettingsFragment();
   1023         final DialogFragment f =
   1024                 CheckSettingsErrorDialogFragment.newInstance(reason, message);
   1025         f.show(getFragmentManager(), CheckSettingsErrorDialogFragment.TAG);
   1026     }
   1027 
   1028     @Override
   1029     public void onCheckSettingsErrorDialogEditCertificate() {
   1030         if (mState == STATE_CHECKING_PRECONFIGURED) {
   1031             mPreConfiguredFailed = true;
   1032             proceed();
   1033         } else {
   1034             resetStateFromCurrentFragment();
   1035         }
   1036         final AccountSetupIncomingFragment f = (AccountSetupIncomingFragment) getContentFragment();
   1037         f.onCertificateRequested();
   1038     }
   1039 
   1040     @Override
   1041     public void onCheckSettingsErrorDialogEditSettings() {
   1042         // If we're checking pre-configured, set a flag that we failed and navigate forwards to
   1043         // incoming settings
   1044         if (mState == STATE_CHECKING_PRECONFIGURED || mState == STATE_AUTO_DISCOVER) {
   1045             mPreConfiguredFailed = true;
   1046             proceed();
   1047         } else {
   1048             resetStateFromCurrentFragment();
   1049         }
   1050     }
   1051 
   1052     @Override
   1053     public void onCheckSettingsComplete() {
   1054         mPreConfiguredFailed = false;
   1055         mPasswordFailed = false;
   1056         dismissCheckSettingsFragment();
   1057         proceed();
   1058     }
   1059 
   1060     @Override
   1061     public void onCheckSettingsAutoDiscoverComplete(int result) {
   1062         dismissCheckSettingsFragment();
   1063         proceed();
   1064     }
   1065 
   1066     @Override
   1067     public void onCheckSettingsSecurityRequired(String hostName) {
   1068         dismissCheckSettingsFragment();
   1069         final DialogFragment f = SecurityRequiredDialogFragment.newInstance(hostName);
   1070         f.show(getFragmentManager(), SecurityRequiredDialogFragment.TAG);
   1071     }
   1072 
   1073     @Override
   1074     public void onSecurityRequiredDialogResult(boolean ok) {
   1075         if (ok) {
   1076             proceed();
   1077         } else {
   1078             resetStateFromCurrentFragment();
   1079         }
   1080     }
   1081 
   1082     @Override
   1083     public void onChooseProtocol(String protocol) {
   1084         mSetupData.setIncomingProtocol(this, protocol);
   1085         final Account account = mSetupData.getAccount();
   1086         setDefaultsForProtocol(account);
   1087         proceed();
   1088     }
   1089 
   1090     @Override
   1091     public void onABProtocolDisambiguated(String chosenProtocol) {
   1092         if (!TextUtils.equals(mSetupData.getIncomingProtocol(this), chosenProtocol)) {
   1093             mIsPreConfiguredProvider = false;
   1094             mSetupData.setIncomingProtocol(this, chosenProtocol);
   1095             final Account account = mSetupData.getAccount();
   1096             setDefaultsForProtocol(account);
   1097         }
   1098         proceed();
   1099     }
   1100 
   1101     /**
   1102      * Ths is called when the user clicks the "done" button.
   1103      * It collects the data from the UI, updates the setup account record, and launches a fragment
   1104      * which handles creating the account in the system and database.
   1105      */
   1106     private void initiateAccountCreation() {
   1107         mIsProcessing = true;
   1108         getContentFragment().setNextButtonEnabled(false);
   1109 
   1110         final Account account = mSetupData.getAccount();
   1111         if (account.mHostAuthRecv == null) {
   1112             throw new IllegalStateException("in AccountSetupOptions with null mHostAuthRecv");
   1113         }
   1114 
   1115         final AccountSetupOptionsFragment fragment = (AccountSetupOptionsFragment)
   1116                 getContentFragment();
   1117         if (fragment == null) {
   1118             throw new IllegalStateException("Fragment missing!");
   1119         }
   1120 
   1121         account.setDisplayName(account.getEmailAddress());
   1122         int newFlags = account.getFlags() & ~(Account.FLAGS_BACKGROUND_ATTACHMENTS);
   1123         final EmailServiceUtils.EmailServiceInfo serviceInfo =
   1124                 mSetupData.getIncomingServiceInfo(this);
   1125         if (serviceInfo.offerAttachmentPreload && fragment.getBackgroundAttachmentsValue()) {
   1126             newFlags |= Account.FLAGS_BACKGROUND_ATTACHMENTS;
   1127         }
   1128         final HostAuth hostAuth = account.getOrCreateHostAuthRecv(this);
   1129         if (hostAuth.mProtocol.equals(getString(R.string.protocol_eas))) {
   1130             try {
   1131                 final double protocolVersionDouble = Double.parseDouble(account.mProtocolVersion);
   1132                 if (protocolVersionDouble >= 12.0) {
   1133                     // If the the account is EAS and the protocol version is above 12.0,
   1134                     // we know that SmartForward is enabled and the various search flags
   1135                     // should be enabled first.
   1136                     // TODO: Move this into protocol specific code in the future.
   1137                     newFlags |= Account.FLAGS_SUPPORTS_SMART_FORWARD |
   1138                             Account.FLAGS_SUPPORTS_GLOBAL_SEARCH | Account.FLAGS_SUPPORTS_SEARCH;
   1139                 }
   1140             } catch (NumberFormatException e) {
   1141                 LogUtils.wtf(LogUtils.TAG, e, "Exception thrown parsing the protocol version.");
   1142             }
   1143         }
   1144         account.setFlags(newFlags);
   1145         account.setSyncInterval(fragment.getCheckFrequencyValue());
   1146         final Integer syncWindowValue = fragment.getAccountSyncWindowValue();
   1147         if (syncWindowValue != null) {
   1148             account.setSyncLookback(syncWindowValue);
   1149         }
   1150 
   1151         // Finish setting up the account, and commit it to the database
   1152         if (mSetupData.getPolicy() != null) {
   1153             account.mFlags |= Account.FLAGS_SECURITY_HOLD;
   1154             account.mPolicy = mSetupData.getPolicy();
   1155         }
   1156 
   1157         // Finally, write the completed account (for the first time) and then
   1158         // install it into the Account manager as well.  These are done off-thread.
   1159         // The account manager will report back via the callback, which will take us to
   1160         // the next operations.
   1161         final boolean syncEmail = fragment.getSyncEmailValue();
   1162         final boolean syncCalendar = serviceInfo.syncCalendar && fragment.getSyncCalendarValue();
   1163         final boolean syncContacts = serviceInfo.syncContacts && fragment.getSyncContactsValue();
   1164         final boolean enableNotifications = fragment.getNotifyValue();
   1165 
   1166         final Fragment f = AccountCreationFragment.newInstance(account, syncEmail, syncCalendar,
   1167                 syncContacts, enableNotifications);
   1168         final FragmentTransaction ft = getFragmentManager().beginTransaction();
   1169         ft.add(f, AccountCreationFragment.TAG);
   1170         ft.commit();
   1171 
   1172         showCreateAccountDialog();
   1173     }
   1174 
   1175     /**
   1176      * Called by the account creation fragment after it has completed.
   1177      * We do a small amount of work here before moving on to the next state.
   1178      */
   1179     @Override
   1180     public void onAccountCreationFragmentComplete() {
   1181         destroyAccountCreationFragment();
   1182         // If the account manager initiated the creation, and success was not reported,
   1183         // then we assume that we're giving up (for any reason) - report failure.
   1184         if (mAccountAuthenticatorResponse != null) {
   1185             final EmailServiceUtils.EmailServiceInfo info = mSetupData.getIncomingServiceInfo(this);
   1186             final Bundle b = new Bundle(2);
   1187             b.putString(AccountManager.KEY_ACCOUNT_NAME, mSetupData.getEmail());
   1188             b.putString(AccountManager.KEY_ACCOUNT_TYPE, info.accountType);
   1189             mAccountAuthenticatorResponse.onResult(b);
   1190             mAccountAuthenticatorResponse = null;
   1191             mReportAccountAuthenticatorError = false;
   1192         }
   1193         setResult(RESULT_OK);
   1194         proceed();
   1195     }
   1196 
   1197     @Override
   1198     public void destroyAccountCreationFragment() {
   1199         dismissCreateAccountDialog();
   1200 
   1201         final Fragment f = getFragmentManager().findFragmentByTag(AccountCreationFragment.TAG);
   1202         if (f == null) {
   1203             LogUtils.wtf(LogUtils.TAG, "Couldn't find AccountCreationFragment to destroy");
   1204         }
   1205         getFragmentManager().beginTransaction()
   1206                 .remove(f)
   1207                 .commit();
   1208     }
   1209 
   1210 
   1211     public static class CreateAccountDialogFragment extends DialogFragment {
   1212         public static final String TAG = "CreateAccountDialogFragment";
   1213         public CreateAccountDialogFragment() {}
   1214 
   1215         public static CreateAccountDialogFragment newInstance() {
   1216             return new CreateAccountDialogFragment();
   1217         }
   1218 
   1219         @Override
   1220         public Dialog onCreateDialog(Bundle savedInstanceState) {
   1221             /// Show "Creating account..." dialog
   1222             setCancelable(false);
   1223             final ProgressDialog d = new ProgressDialog(getActivity());
   1224             d.setIndeterminate(true);
   1225             d.setMessage(getString(R.string.account_setup_creating_account_msg));
   1226             return d;
   1227         }
   1228     }
   1229 
   1230     protected void showCreateAccountDialog() {
   1231         CreateAccountDialogFragment.newInstance()
   1232                 .show(getFragmentManager(), CreateAccountDialogFragment.TAG);
   1233     }
   1234 
   1235     protected void dismissCreateAccountDialog() {
   1236         final DialogFragment f = (DialogFragment)
   1237                 getFragmentManager().findFragmentByTag(CreateAccountDialogFragment.TAG);
   1238         if (f != null) {
   1239             f.dismiss();
   1240         }
   1241     }
   1242 
   1243     public static class CreateAccountErrorDialogFragment extends DialogFragment
   1244             implements DialogInterface.OnClickListener {
   1245         public CreateAccountErrorDialogFragment() {}
   1246 
   1247         @Override
   1248         public Dialog onCreateDialog(Bundle savedInstanceState) {
   1249             final String message = getString(R.string.account_setup_failed_dlg_auth_message,
   1250                     R.string.system_account_create_failed);
   1251 
   1252             setCancelable(false);
   1253             return new AlertDialog.Builder(getActivity())
   1254                     .setIconAttribute(android.R.attr.alertDialogIcon)
   1255                     .setTitle(R.string.account_setup_failed_dlg_title)
   1256                     .setMessage(message)
   1257                     .setPositiveButton(android.R.string.ok, this)
   1258                     .create();
   1259         }
   1260 
   1261         @Override
   1262         public void onClick(DialogInterface dialog, int which) {
   1263             getActivity().finish();
   1264         }
   1265     }
   1266 
   1267     /**
   1268      * This is called if MailService.setupAccountManagerAccount() fails for some reason
   1269      */
   1270     @Override
   1271     public void showCreateAccountErrorDialog() {
   1272         new CreateAccountErrorDialogFragment().show(getFragmentManager(), null);
   1273     }
   1274 
   1275     /**
   1276      * Collect the data from AccountSetupNames and finish up account creation
   1277      */
   1278     private void initiateAccountFinalize() {
   1279         mIsProcessing = true;
   1280         getContentFragment().setNextButtonEnabled(false);
   1281 
   1282         AccountSetupNamesFragment fragment = (AccountSetupNamesFragment) getContentFragment();
   1283         // Update account object from UI
   1284         final Account account = mSetupData.getAccount();
   1285         final String description = fragment.getDescription();
   1286         if (!TextUtils.isEmpty(description)) {
   1287             account.setDisplayName(description);
   1288         }
   1289         account.setSenderName(fragment.getSenderName());
   1290 
   1291         final Fragment f = AccountFinalizeFragment.newInstance(account);
   1292         final FragmentTransaction ft = getFragmentManager().beginTransaction();
   1293         ft.add(f, AccountFinalizeFragment.TAG);
   1294         ft.commit();
   1295     }
   1296 
   1297     /**
   1298      * Called when the AccountFinalizeFragment has finished its tasks
   1299      */
   1300     @Override
   1301     public void onAccountFinalizeFragmentComplete() {
   1302         finish();
   1303     }
   1304 }
   1305