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