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