Home | History | Annotate | Download | only in setup
      1 /*
      2  * Copyright (C) 2008 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.Activity;
     22 import android.app.ActivityManager;
     23 import android.app.AlertDialog;
     24 import android.app.Dialog;
     25 import android.app.DialogFragment;
     26 import android.app.FragmentTransaction;
     27 import android.content.Context;
     28 import android.content.DialogInterface;
     29 import android.content.Intent;
     30 import android.os.AsyncTask;
     31 import android.os.Bundle;
     32 import android.text.Editable;
     33 import android.text.TextUtils;
     34 import android.text.TextWatcher;
     35 import android.util.Log;
     36 import android.view.View;
     37 import android.view.View.OnClickListener;
     38 import android.widget.Button;
     39 import android.widget.CheckBox;
     40 import android.widget.EditText;
     41 import android.widget.TextView;
     42 import android.widget.Toast;
     43 
     44 import com.android.email.EmailAddressValidator;
     45 import com.android.email.R;
     46 import com.android.email.VendorPolicyLoader;
     47 import com.android.email.activity.ActivityHelper;
     48 import com.android.email.activity.UiUtilities;
     49 import com.android.email.activity.Welcome;
     50 import com.android.email.activity.setup.AccountSettingsUtils.Provider;
     51 import com.android.emailcommon.Logging;
     52 import com.android.emailcommon.provider.Account;
     53 import com.android.emailcommon.provider.EmailContent;
     54 import com.android.emailcommon.provider.HostAuth;
     55 import com.android.emailcommon.service.SyncWindow;
     56 import com.android.emailcommon.utility.Utility;
     57 import com.google.common.annotations.VisibleForTesting;
     58 
     59 import java.net.URISyntaxException;
     60 import java.util.concurrent.Callable;
     61 import java.util.concurrent.ExecutionException;
     62 import java.util.concurrent.FutureTask;
     63 
     64 /**
     65  * Prompts the user for the email address and password. Also prompts for "Use this account as
     66  * default" if this is the 2nd+ account being set up.
     67  *
     68  * If the domain is well-known, the account is configured fully and checked immediately
     69  * using AccountCheckSettingsFragment.  If this succeeds we proceed directly to AccountSetupOptions.
     70  *
     71  * If the domain is not known, or the user selects Manual setup, we invoke the
     72  * AccountSetupAccountType activity where the user can begin to manually configure the account.
     73  *
     74  * === Support for automated testing ==
     75  * This activity can also be launched directly via ACTION_CREATE_ACCOUNT.  This is intended
     76  * only for use by continuous test systems, and is currently only available when
     77  * {@link ActivityManager#isRunningInTestHarness()} is set.  To use this mode, you must construct
     78  * an intent which contains all necessary information to create the account.  No connection
     79  * checking is done, so the account may or may not actually work.  Here is a sample command, for a
     80  * gmail account "test_account" with a password of "test_password".
     81  *
     82  *      $ adb shell am start -a com.android.email.CREATE_ACCOUNT \
     83  *          -e EMAIL test_account (at) gmail.com \
     84  *          -e USER "Test Account Name" \
     85  *          -e INCOMING imap+ssl+://test_account:test_password (at) imap.gmail.com \
     86  *          -e OUTGOING smtp+ssl+://test_account:test_password (at) smtp.gmail.com
     87  *
     88  * Note: For accounts that require the full email address in the login, encode the @ as %40.
     89  * Note: Exchange accounts that require device security policies cannot be created automatically.
     90  */
     91 public class AccountSetupBasics extends AccountSetupActivity
     92         implements OnClickListener, TextWatcher, AccountCheckSettingsFragment.Callbacks {
     93 
     94     private final static boolean ENTER_DEBUG_SCREEN = true;
     95 
     96     /**
     97      * Direct access for forcing account creation
     98      * For use by continuous automated test system (e.g. in conjunction with monkey tests)
     99      */
    100     private static final String ACTION_CREATE_ACCOUNT = "com.android.email.CREATE_ACCOUNT";
    101     private static final String EXTRA_FLOW_MODE = "FLOW_MODE";
    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_INCOMING = "INCOMING";
    105     private static final String EXTRA_CREATE_ACCOUNT_OUTGOING = "OUTGOING";
    106     private static final Boolean DEBUG_ALLOW_NON_TEST_HARNESS_CREATION = false;
    107 
    108     private static final String STATE_KEY_PROVIDER = "AccountSetupBasics.provider";
    109 
    110     // NOTE: If you change this value, confirm that the new interval exists in arrays.xml
    111     private static final int DEFAULT_ACCOUNT_CHECK_INTERVAL = 15;
    112 
    113     // Support for UI
    114     private TextView mWelcomeView;
    115     private EditText mEmailView;
    116     private EditText mPasswordView;
    117     private CheckBox mDefaultView;
    118     private final EmailAddressValidator mEmailValidator = new EmailAddressValidator();
    119     private Provider mProvider;
    120     private Button mManualButton;
    121     private Button mNextButton;
    122     private boolean mNextButtonInhibit;
    123     private boolean mPaused;
    124     private boolean mReportAccountAuthenticatorError;
    125 
    126     // FutureTask to look up the owner
    127     FutureTask<String> mOwnerLookupTask;
    128 
    129     public static void actionNewAccount(Activity fromActivity) {
    130         Intent i = new Intent(fromActivity, AccountSetupBasics.class);
    131         i.putExtra(EXTRA_FLOW_MODE, SetupData.FLOW_MODE_NORMAL);
    132         fromActivity.startActivity(i);
    133     }
    134 
    135     /**
    136      * This generates setup data that can be used to start a self-contained account creation flow
    137      * for exchange accounts.
    138      */
    139     public static Intent actionSetupExchangeIntent(Context context) {
    140         Intent i = new Intent(context, AccountSetupBasics.class);
    141         i.putExtra(EXTRA_FLOW_MODE, SetupData.FLOW_MODE_ACCOUNT_MANAGER_EAS);
    142         return i;
    143     }
    144 
    145     /**
    146      * This generates setup data that can be used to start a self-contained account creation flow
    147      * for pop/imap accounts.
    148      */
    149     public static Intent actionSetupPopImapIntent(Context context) {
    150         Intent i = new Intent(context, AccountSetupBasics.class);
    151         i.putExtra(EXTRA_FLOW_MODE, SetupData.FLOW_MODE_ACCOUNT_MANAGER_POP_IMAP);
    152         return i;
    153     }
    154 
    155     public static void actionAccountCreateFinishedAccountFlow(Activity fromActivity) {
    156         // TODO: handle this case - modifying state on SetupData when instantiating an Intent
    157         // is not safe, since it's not guaranteed that an Activity will run with the Intent, and
    158         // information can get lost.
    159 
    160         Intent i= new Intent(fromActivity, AccountSetupBasics.class);
    161         // If we're in the "account flow" (from AccountManager), we want to return to the caller
    162         // (in the settings app)
    163         SetupData.init(SetupData.FLOW_MODE_RETURN_TO_CALLER);
    164         i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    165         fromActivity.startActivity(i);
    166     }
    167 
    168     public static void actionAccountCreateFinished(final Activity fromActivity,
    169             final long accountId) {
    170         Utility.runAsync(new Runnable() {
    171            public void run() {
    172                Intent i = new Intent(fromActivity, AccountSetupBasics.class);
    173                // If we're not in the "account flow" (from AccountManager), we want to show the
    174                // message list for the new inbox
    175                Account account = Account.restoreAccountWithId(fromActivity, accountId);
    176                SetupData.init(SetupData.FLOW_MODE_RETURN_TO_MESSAGE_LIST, account);
    177                i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    178                fromActivity.startActivity(i);
    179             }});
    180     }
    181 
    182     @Override
    183     public void onCreate(Bundle savedInstanceState) {
    184         super.onCreate(savedInstanceState);
    185         ActivityHelper.debugSetWindowFlags(this);
    186 
    187         // Check for forced account creation first, as it comes from an externally-generated
    188         // intent and won't have any SetupData prepared.
    189         String action = getIntent().getAction();
    190         if (ACTION_CREATE_ACCOUNT.equals(action)) {
    191             SetupData.init(SetupData.FLOW_MODE_FORCE_CREATE);
    192         }
    193 
    194         int flowMode = getIntent().getIntExtra(EXTRA_FLOW_MODE, SetupData.FLOW_MODE_UNSPECIFIED);
    195         if (flowMode != SetupData.FLOW_MODE_UNSPECIFIED) {
    196             SetupData.init(flowMode);
    197         } else {
    198             // TODO: get rid of this case. It's not safe to rely on this global static state. It
    199             // should be specified in the Intent always.
    200             flowMode = SetupData.getFlowMode();
    201         }
    202 
    203         if (flowMode == SetupData.FLOW_MODE_RETURN_TO_CALLER) {
    204             // Return to the caller who initiated account creation
    205             finish();
    206             return;
    207         } else if (flowMode == SetupData.FLOW_MODE_RETURN_TO_MESSAGE_LIST) {
    208             Account account = SetupData.getAccount();
    209             if (account != null && account.mId >= 0) {
    210                 // Show the message list for the new account
    211                 Welcome.actionOpenAccountInbox(this, account.mId);
    212                 finish();
    213                 return;
    214             }
    215         }
    216 
    217         setContentView(R.layout.account_setup_basics);
    218 
    219         mWelcomeView = (TextView) UiUtilities.getView(this, R.id.instructions);
    220         mEmailView = (EditText) UiUtilities.getView(this, R.id.account_email);
    221         mPasswordView = (EditText) UiUtilities.getView(this, R.id.account_password);
    222         mDefaultView = (CheckBox) UiUtilities.getView(this, R.id.account_default);
    223 
    224         mEmailView.addTextChangedListener(this);
    225         mPasswordView.addTextChangedListener(this);
    226 
    227         // If there are one or more accounts already in existence, then display
    228         // the "use as default" checkbox (it defaults to hidden).
    229         new DisplayCheckboxTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    230 
    231         boolean manualButtonDisplayed = true;
    232         boolean alternateStrings = false;
    233         if (flowMode == SetupData.FLOW_MODE_ACCOUNT_MANAGER_EAS) {
    234             // No need for manual button -> next is appropriate
    235             manualButtonDisplayed = false;
    236             // Swap welcome text for EAS-specific text
    237             alternateStrings = VendorPolicyLoader.getInstance(this).useAlternateExchangeStrings();
    238             setTitle(alternateStrings
    239                     ? R.string.account_setup_basics_exchange_title_alternate
    240                     : R.string.account_setup_basics_exchange_title);
    241             mWelcomeView.setText(alternateStrings
    242                     ? R.string.accounts_welcome_exchange_alternate
    243                     : R.string.accounts_welcome_exchange);
    244         }
    245 
    246         // Configure buttons
    247         mManualButton = (Button) UiUtilities.getView(this, R.id.manual_setup);
    248         mNextButton = (Button) UiUtilities.getView(this, R.id.next);
    249         mManualButton.setVisibility(manualButtonDisplayed ? View.VISIBLE : View.INVISIBLE);
    250         mManualButton.setOnClickListener(this);
    251         mNextButton.setOnClickListener(this);
    252         // Force disabled until validator notifies otherwise
    253         onEnableProceedButtons(false);
    254         // Lightweight debounce while Async tasks underway
    255         mNextButtonInhibit = false;
    256 
    257         // Set aside incoming AccountAuthenticatorResponse, if there was any
    258         AccountAuthenticatorResponse authenticatorResponse =
    259             getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
    260         SetupData.setAccountAuthenticatorResponse(authenticatorResponse);
    261         if (authenticatorResponse != null) {
    262             // When this Activity is called as part of account authentification flow,
    263             // we are responsible for eventually reporting the result (success or failure) to
    264             // the account manager.  Most exit paths represent an failed or abandoned setup,
    265             // so the default is to report the error.  Success will be reported by the code in
    266             // AccountSetupOptions that commits the finally created account.
    267             mReportAccountAuthenticatorError = true;
    268         }
    269 
    270         // Load fields, but only once
    271         String userName = SetupData.getUsername();
    272         if (userName != null) {
    273             mEmailView.setText(userName);
    274             SetupData.setUsername(null);
    275         }
    276         String password = SetupData.getPassword();
    277         if (userName != null) {
    278             mPasswordView.setText(password);
    279             SetupData.setPassword(null);
    280         }
    281 
    282         // Handle force account creation immediately (now that fragment is set up)
    283         // This is never allowed in a normal user build and will exit immediately.
    284         if (SetupData.getFlowMode() == SetupData.FLOW_MODE_FORCE_CREATE) {
    285             if (!DEBUG_ALLOW_NON_TEST_HARNESS_CREATION &&
    286                     !ActivityManager.isRunningInTestHarness()) {
    287                 Log.e(Logging.LOG_TAG,
    288                         "ERROR: Force account create only allowed while in test harness");
    289                 finish();
    290                 return;
    291             }
    292             Intent intent = getIntent();
    293             String email = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_EMAIL);
    294             String user = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_USER);
    295             String incoming = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_INCOMING);
    296             String outgoing = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_OUTGOING);
    297             if (TextUtils.isEmpty(email) || TextUtils.isEmpty(user) ||
    298                     TextUtils.isEmpty(incoming) || TextUtils.isEmpty(outgoing)) {
    299                 Log.e(Logging.LOG_TAG, "ERROR: Force account create requires extras EMAIL, USER, " +
    300                         "INCOMING, OUTGOING");
    301                 finish();
    302                 return;
    303             }
    304             forceCreateAccount(email, user, incoming, outgoing);
    305             onCheckSettingsComplete(AccountCheckSettingsFragment.CHECK_SETTINGS_OK); // calls finish
    306             return;
    307         }
    308 
    309         if (savedInstanceState != null && savedInstanceState.containsKey(STATE_KEY_PROVIDER)) {
    310             mProvider = (Provider) savedInstanceState.getSerializable(STATE_KEY_PROVIDER);
    311         }
    312 
    313         // Launch a worker to look up the owner name.  It should be ready well in advance of
    314         // the time the user clicks next or manual.
    315         mOwnerLookupTask = new FutureTask<String>(mOwnerLookupCallable);
    316         Utility.runAsync(mOwnerLookupTask);
    317     }
    318 
    319     @Override
    320     public void onPause() {
    321         super.onPause();
    322         mPaused = true;
    323     }
    324 
    325     @Override
    326     public void onResume() {
    327         super.onResume();
    328         mPaused = false;
    329     }
    330 
    331     @Override
    332     public void finish() {
    333         // If the account manager initiated the creation, and success was not reported,
    334         // then we assume that we're giving up (for any reason) - report failure.
    335         if (mReportAccountAuthenticatorError) {
    336             AccountAuthenticatorResponse authenticatorResponse =
    337                     SetupData.getAccountAuthenticatorResponse();
    338             if (authenticatorResponse != null) {
    339                 authenticatorResponse.onError(AccountManager.ERROR_CODE_CANCELED, "canceled");
    340                 SetupData.setAccountAuthenticatorResponse(null);
    341             }
    342         }
    343         super.finish();
    344     }
    345 
    346     @Override
    347     public void onSaveInstanceState(Bundle outState) {
    348         super.onSaveInstanceState(outState);
    349         if (mProvider != null) {
    350             outState.putSerializable(STATE_KEY_PROVIDER, mProvider);
    351         }
    352     }
    353 
    354     /**
    355      * Implements OnClickListener
    356      */
    357     @Override
    358     public void onClick(View v) {
    359         switch (v.getId()) {
    360             case R.id.next:
    361                 // Simple debounce - just ignore while async checks are underway
    362                 if (mNextButtonInhibit) {
    363                     return;
    364                 }
    365                 onNext();
    366                 break;
    367             case R.id.manual_setup:
    368                 onManualSetup(false);
    369                 break;
    370         }
    371     }
    372 
    373     /**
    374      * Implements TextWatcher
    375      */
    376     public void afterTextChanged(Editable s) {
    377         validateFields();
    378     }
    379 
    380     /**
    381      * Implements TextWatcher
    382      */
    383     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    384     }
    385 
    386     /**
    387      * Implements TextWatcher
    388      */
    389     public void onTextChanged(CharSequence s, int start, int before, int count) {
    390     }
    391 
    392     private void validateFields() {
    393         boolean valid = Utility.isTextViewNotEmpty(mEmailView)
    394                 && Utility.isTextViewNotEmpty(mPasswordView)
    395                 && mEmailValidator.isValid(mEmailView.getText().toString().trim());
    396         onEnableProceedButtons(valid);
    397 
    398         // Warn (but don't prevent) if password has leading/trailing spaces
    399         AccountSettingsUtils.checkPasswordSpaces(this, mPasswordView);
    400     }
    401 
    402     /**
    403      * Return an existing username if found, or null.  This is the result of the Callable (below).
    404      */
    405     private String getOwnerName() {
    406         String result = null;
    407         try {
    408             result = mOwnerLookupTask.get();
    409         } catch (InterruptedException e) {
    410         } catch (ExecutionException e) {
    411         }
    412         return result;
    413     }
    414 
    415     /**
    416      * Callable that returns the username (based on other accounts) or null.
    417      */
    418     private final Callable<String> mOwnerLookupCallable = new Callable<String>() {
    419         public String call() {
    420             Context context = AccountSetupBasics.this;
    421             String name = null;
    422             long defaultId = Account.getDefaultAccountId(context);
    423             if (defaultId != -1) {
    424                 Account account = Account.restoreAccountWithId(context, defaultId);
    425                 if (account != null) {
    426                     name = account.getSenderName();
    427                 }
    428             }
    429             return name;
    430         }
    431     };
    432 
    433     /**
    434      * Finish the auto setup process, in some cases after showing a warning dialog.
    435      */
    436     private void finishAutoSetup() {
    437         String email = mEmailView.getText().toString().trim();
    438         String password = mPasswordView.getText().toString();
    439 
    440         try {
    441             mProvider.expandTemplates(email);
    442 
    443             Account account = SetupData.getAccount();
    444             HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
    445             HostAuth.setHostAuthFromString(recvAuth, mProvider.incomingUri);
    446             recvAuth.setLogin(mProvider.incomingUsername, password);
    447 
    448             HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
    449             HostAuth.setHostAuthFromString(sendAuth, mProvider.outgoingUri);
    450             sendAuth.setLogin(mProvider.outgoingUsername, password);
    451 
    452             // Populate the setup data, assuming that the duplicate account check will succeed
    453             populateSetupData(getOwnerName(), email, mDefaultView.isChecked());
    454 
    455             // Stop here if the login credentials duplicate an existing account
    456             // Launch an Async task to do the work
    457             new DuplicateCheckTask(this, recvAuth.mAddress, mProvider.incomingUsername)
    458                     .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    459         } catch (URISyntaxException e) {
    460             /*
    461              * If there is some problem with the URI we give up and go on to manual setup.
    462              * Technically speaking, AutoDiscover is OK here, since the user clicked "Next"
    463              * to get here. This will not happen in practice because we don't expect to
    464              * find any EAS accounts in the providers list.
    465              */
    466             onManualSetup(true);
    467         }
    468     }
    469 
    470     /**
    471      * Async task that continues the work of finishAutoSetup().  Checks for a duplicate
    472      * account and then either alerts the user, or continues.
    473      */
    474     private class DuplicateCheckTask extends AsyncTask<Void, Void, Account> {
    475         private final Context mContext;
    476         private final String mCheckHost;
    477         private final String mCheckLogin;
    478 
    479         public DuplicateCheckTask(Context context, String checkHost, String checkLogin) {
    480             mContext = context;
    481             mCheckHost = checkHost;
    482             mCheckLogin = checkLogin;
    483             // Prevent additional clicks on the next button during Async lookup
    484             mNextButtonInhibit = true;
    485         }
    486 
    487         @Override
    488         protected Account doInBackground(Void... params) {
    489             Account account = Utility.findExistingAccount(mContext, -1,
    490                     mCheckHost, mCheckLogin);
    491             return account;
    492         }
    493 
    494         @Override
    495         protected void onPostExecute(Account duplicateAccount) {
    496             mNextButtonInhibit = false;
    497             // Exit immediately if the user left before we finished
    498             if (mPaused) return;
    499             // Show duplicate account warning, or proceed
    500             if (duplicateAccount != null) {
    501                 DuplicateAccountDialogFragment dialogFragment =
    502                     DuplicateAccountDialogFragment.newInstance(duplicateAccount.mDisplayName);
    503                 dialogFragment.show(getFragmentManager(), DuplicateAccountDialogFragment.TAG);
    504                 return;
    505             } else {
    506                 AccountCheckSettingsFragment checkerFragment =
    507                     AccountCheckSettingsFragment.newInstance(
    508                         SetupData.CHECK_INCOMING | SetupData.CHECK_OUTGOING, null);
    509                 FragmentTransaction transaction = getFragmentManager().beginTransaction();
    510                 transaction.add(checkerFragment, AccountCheckSettingsFragment.TAG);
    511                 transaction.addToBackStack("back");
    512                 transaction.commit();
    513             }
    514         }
    515     }
    516 
    517 
    518     /**
    519      * When "next" button is clicked
    520      */
    521     private void onNext() {
    522         // Try auto-configuration from XML providers (unless in EAS mode, we can skip it)
    523         if (SetupData.getFlowMode() != SetupData.FLOW_MODE_ACCOUNT_MANAGER_EAS) {
    524             String email = mEmailView.getText().toString().trim();
    525             String[] emailParts = email.split("@");
    526             String domain = emailParts[1].trim();
    527             mProvider = AccountSettingsUtils.findProviderForDomain(this, domain);
    528             if (mProvider != null) {
    529                 if (mProvider.note != null) {
    530                     NoteDialogFragment dialogFragment =
    531                             NoteDialogFragment.newInstance(mProvider.note);
    532                     dialogFragment.show(getFragmentManager(), NoteDialogFragment.TAG);
    533                 } else {
    534                     finishAutoSetup();
    535                 }
    536                 return;
    537             }
    538         }
    539         // Can't use auto setup (although EAS accounts may still be able to AutoDiscover)
    540         onManualSetup(true);
    541     }
    542 
    543     /**
    544      * When "manual setup" button is clicked
    545      *
    546      * @param allowAutoDiscover - true if the user clicked 'next' and (if the account is EAS)
    547      * it's OK to use autodiscover.  false to prevent autodiscover and go straight to manual setup.
    548      * Ignored for IMAP & POP accounts.
    549      */
    550     private void onManualSetup(boolean allowAutoDiscover) {
    551         String email = mEmailView.getText().toString().trim();
    552         String password = mPasswordView.getText().toString();
    553         String[] emailParts = email.split("@");
    554         String user = emailParts[0].trim();
    555         String domain = emailParts[1].trim();
    556 
    557         // Alternate entry to the debug options screen (for devices without a physical keyboard:
    558         //  Username: d (at) d.d
    559         //  Password: debug
    560         if (ENTER_DEBUG_SCREEN && "d (at) d.d".equals(email) && "debug".equals(password)) {
    561             mEmailView.setText("");
    562             mPasswordView.setText("");
    563             AccountSettings.actionSettingsWithDebug(this);
    564             return;
    565         }
    566 
    567         Account account = SetupData.getAccount();
    568         HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
    569         recvAuth.setLogin(user, password);
    570         recvAuth.setConnection("placeholder", domain, HostAuth.PORT_UNKNOWN, HostAuth.FLAG_NONE);
    571 
    572         HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
    573         sendAuth.setLogin(user, password);
    574         sendAuth.setConnection("placeholder", domain, HostAuth.PORT_UNKNOWN, HostAuth.FLAG_NONE);
    575 
    576         populateSetupData(getOwnerName(), email, mDefaultView.isChecked());
    577 
    578         SetupData.setAllowAutodiscover(allowAutoDiscover);
    579         AccountSetupAccountType.actionSelectAccountType(this);
    580     }
    581 
    582     /**
    583      * To support continuous testing, we allow the forced creation of accounts.
    584      * This works in a manner fairly similar to automatic setup, in which the complete server
    585      * Uri's are available, except that we will also skip checking (as if both checks were true)
    586      * and all other UI.
    587      *
    588      * @param email The email address for the new account
    589      * @param user The user name for the new account
    590      * @param incoming The URI-style string defining the incoming account
    591      * @param outgoing The URI-style string defining the outgoing account
    592      */
    593     private void forceCreateAccount(String email, String user, String incoming, String outgoing) {
    594         Account account = SetupData.getAccount();
    595         try {
    596             HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
    597             HostAuth.setHostAuthFromString(recvAuth, incoming);
    598 
    599             HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
    600             HostAuth.setHostAuthFromString(sendAuth, outgoing);
    601 
    602             populateSetupData(user, email, false);
    603         } catch (URISyntaxException e) {
    604             // If we can't set up the URL, don't continue - account setup pages will fail too
    605             Toast.makeText(
    606                     this, R.string.account_setup_username_password_toast, Toast.LENGTH_LONG).show();
    607         }
    608     }
    609 
    610     /**
    611      * Populate SetupData's account with complete setup info.
    612      */
    613     private void populateSetupData(String senderName, String senderEmail, boolean isDefault) {
    614         Account account = SetupData.getAccount();
    615         account.setSenderName(senderName);
    616         account.setEmailAddress(senderEmail);
    617         account.setDisplayName(senderEmail);
    618         account.setDefaultAccount(isDefault);
    619         SetupData.setDefault(isDefault);        // TODO - why duplicated, if already set in account
    620 
    621         String protocol = account.mHostAuthRecv.mProtocol;
    622         setFlagsForProtocol(account, protocol);
    623     }
    624 
    625     /**
    626      * Sets the account sync, delete, and other misc flags not captured in {@code HostAuth}
    627      * information for the specified account based on the protocol type.
    628      */
    629     @VisibleForTesting
    630     static void setFlagsForProtocol(Account account, String protocol) {
    631         if (HostAuth.SCHEME_IMAP.equals(protocol)) {
    632             // Delete policy must be set explicitly, because IMAP does not provide a UI selection
    633             // for it.
    634             account.setDeletePolicy(Account.DELETE_POLICY_ON_DELETE);
    635             account.mFlags |= Account.FLAGS_SUPPORTS_SEARCH;
    636         }
    637 
    638         if (HostAuth.SCHEME_EAS.equals(protocol)) {
    639             account.setDeletePolicy(Account.DELETE_POLICY_ON_DELETE);
    640             account.setSyncInterval(Account.CHECK_INTERVAL_PUSH);
    641             account.setSyncLookback(SyncWindow.SYNC_WINDOW_AUTO);
    642         } else {
    643             account.setSyncInterval(DEFAULT_ACCOUNT_CHECK_INTERVAL);
    644         }
    645     }
    646 
    647     /**
    648      * Implements AccountCheckSettingsFragment.Callbacks
    649      *
    650      * This is used in automatic setup mode to jump directly down to the options screen.
    651      *
    652      * This is the only case where we finish() this activity but account setup is continuing,
    653      * so we inhibit reporting any error back to the Account manager.
    654      */
    655     @Override
    656     public void onCheckSettingsComplete(int result) {
    657         if (result == AccountCheckSettingsFragment.CHECK_SETTINGS_OK) {
    658             AccountSetupOptions.actionOptions(this);
    659             mReportAccountAuthenticatorError = false;
    660             finish();
    661         }
    662     }
    663 
    664     /**
    665      * Implements AccountCheckSettingsFragment.Callbacks
    666      * This is overridden only by AccountSetupExchange
    667      */
    668     @Override
    669     public void onAutoDiscoverComplete(int result, HostAuth hostAuth) {
    670         throw new IllegalStateException();
    671     }
    672 
    673     /**
    674      * AsyncTask checks count of accounts and displays "use this account as default" checkbox
    675      * if there are more than one.
    676      */
    677     private class DisplayCheckboxTask extends AsyncTask<Void, Void, Integer> {
    678 
    679         @Override
    680         protected Integer doInBackground(Void... params) {
    681             return EmailContent.count(AccountSetupBasics.this, Account.CONTENT_URI);
    682         }
    683 
    684         @Override
    685         protected void onPostExecute(Integer numAccounts) {
    686             if (numAccounts > 0) {
    687                 Activity a = AccountSetupBasics.this;
    688                 UiUtilities.setVisibilitySafe(mDefaultView, View.VISIBLE);
    689                 UiUtilities.setVisibilitySafe(a, R.id.account_default_divider_1, View.VISIBLE);
    690                 UiUtilities.setVisibilitySafe(a, R.id.account_default_divider_2, View.VISIBLE);
    691             }
    692         }
    693     }
    694 
    695     private void onEnableProceedButtons(boolean enabled) {
    696         mManualButton.setEnabled(enabled);
    697         mNextButton.setEnabled(enabled);
    698     }
    699 
    700     /**
    701      * Dialog fragment to show "setup note" dialog
    702      */
    703     public static class NoteDialogFragment extends DialogFragment {
    704         private final static String TAG = "NoteDialogFragment";
    705 
    706         // Argument bundle keys
    707         private final static String BUNDLE_KEY_NOTE = "NoteDialogFragment.Note";
    708 
    709         /**
    710          * Create the dialog with parameters
    711          */
    712         public static NoteDialogFragment newInstance(String note) {
    713             NoteDialogFragment f = new NoteDialogFragment();
    714             Bundle b = new Bundle();
    715             b.putString(BUNDLE_KEY_NOTE, note);
    716             f.setArguments(b);
    717             return f;
    718         }
    719 
    720         @Override
    721         public Dialog onCreateDialog(Bundle savedInstanceState) {
    722             Context context = getActivity();
    723             final String note = getArguments().getString(BUNDLE_KEY_NOTE);
    724 
    725             return new AlertDialog.Builder(context)
    726                 .setIconAttribute(android.R.attr.alertDialogIcon)
    727                 .setTitle(android.R.string.dialog_alert_title)
    728                 .setMessage(note)
    729                 .setPositiveButton(
    730                         R.string.okay_action,
    731                         new DialogInterface.OnClickListener() {
    732                             public void onClick(DialogInterface dialog, int which) {
    733                                 Activity a = getActivity();
    734                                 if (a instanceof AccountSetupBasics) {
    735                                     ((AccountSetupBasics)a).finishAutoSetup();
    736                                 }
    737                                 dismiss();
    738                             }
    739                         })
    740                 .setNegativeButton(
    741                         context.getString(R.string.cancel_action),
    742                         null)
    743                 .create();
    744         }
    745     }
    746 }
    747