AccountSetupBasics.java revision bc47398187c6ffd132435e51d8d61e6ec79a79db
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
17package com.android.email.activity.setup;
18
19import android.accounts.AccountAuthenticatorResponse;
20import android.accounts.AccountManager;
21import android.app.Activity;
22import android.app.ActivityManager;
23import android.app.AlertDialog;
24import android.app.Dialog;
25import android.app.DialogFragment;
26import android.app.FragmentTransaction;
27import android.content.Context;
28import android.content.DialogInterface;
29import android.content.Intent;
30import android.os.AsyncTask;
31import android.os.Bundle;
32import android.text.Editable;
33import android.text.TextUtils;
34import android.text.TextWatcher;
35import android.util.Log;
36import android.view.View;
37import android.view.View.OnClickListener;
38import android.widget.Button;
39import android.widget.CheckBox;
40import android.widget.EditText;
41import android.widget.TextView;
42import android.widget.Toast;
43
44import com.android.email.EmailAddressValidator;
45import com.android.email.R;
46import com.android.email.VendorPolicyLoader;
47import com.android.email.activity.ActivityHelper;
48import com.android.email.activity.UiUtilities;
49import com.android.email.activity.Welcome;
50import com.android.email.activity.setup.AccountSettingsUtils.Provider;
51import com.android.emailcommon.Logging;
52import com.android.emailcommon.provider.Account;
53import com.android.emailcommon.provider.EmailContent;
54import com.android.emailcommon.provider.HostAuth;
55import com.android.emailcommon.service.SyncWindow;
56import com.android.emailcommon.utility.Utility;
57import com.google.common.annotations.VisibleForTesting;
58
59import java.net.URISyntaxException;
60import java.util.concurrent.Callable;
61import java.util.concurrent.ExecutionException;
62import 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@gmail.com \
84 *          -e USER "Test Account Name" \
85 *          -e INCOMING imap+ssl+://test_account:test_password@imap.gmail.com \
86 *          -e OUTGOING smtp+ssl+://test_account:test_password@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 */
91public 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@d.d
559        //  Password: debug
560        if (ENTER_DEBUG_SCREEN && "d@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