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