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