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