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