1 /* 2 * Copyright (C) 2014 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.ActivityManager; 22 import android.app.AlertDialog; 23 import android.app.Dialog; 24 import android.app.DialogFragment; 25 import android.app.Fragment; 26 import android.app.FragmentManager; 27 import android.app.FragmentTransaction; 28 import android.app.LoaderManager; 29 import android.app.ProgressDialog; 30 import android.content.Context; 31 import android.content.CursorLoader; 32 import android.content.DialogInterface; 33 import android.content.Intent; 34 import android.content.Loader; 35 import android.database.Cursor; 36 import android.os.Bundle; 37 import android.provider.ContactsContract; 38 import android.text.TextUtils; 39 import android.view.View; 40 import android.view.inputmethod.InputMethodManager; 41 import android.widget.Toast; 42 43 import com.android.email.R; 44 import com.android.email.service.EmailServiceUtils; 45 import com.android.emailcommon.VendorPolicyLoader; 46 import com.android.emailcommon.provider.Account; 47 import com.android.emailcommon.provider.EmailContent.AccountColumns; 48 import com.android.emailcommon.provider.HostAuth; 49 import com.android.emailcommon.service.SyncWindow; 50 import com.android.mail.utils.LogUtils; 51 52 import java.net.URISyntaxException; 53 import java.util.HashMap; 54 import java.util.Map; 55 56 public class AccountSetupFinal extends AccountSetupActivity 57 implements AccountFinalizeFragment.Callback, 58 AccountSetupNoteDialogFragment.Callback, AccountCreationFragment.Callback, 59 AccountCheckSettingsFragment.Callback, SecurityRequiredDialogFragment.Callback, 60 CheckSettingsErrorDialogFragment.Callback, CheckSettingsProgressDialogFragment.Callback, 61 AccountSetupTypeFragment.Callback, AccountSetupNamesFragment.Callback, 62 AccountSetupOptionsFragment.Callback, AccountSetupBasicsFragment.Callback, 63 AccountServerBaseFragment.Callback, AccountSetupCredentialsFragment.Callback, 64 DuplicateAccountDialogFragment.Callback, AccountSetupABFragment.Callback { 65 66 /** 67 * Direct access for forcing account creation 68 * For use by continuous automated test system (e.g. in conjunction with monkey tests) 69 * 70 * === Support for automated testing == 71 * This activity can also be launched directly via INTENT_FORCE_CREATE_ACCOUNT. This is intended 72 * only for use by continuous test systems, and is currently only available when 73 * {@link ActivityManager#isRunningInTestHarness()} is set. To use this mode, you must 74 * construct an intent which contains all necessary information to create the account. No 75 * connection checking is done, so the account may or may not actually work. Here is a sample 76 * command, for a gmail account "test_account" with a password of "test_password". 77 * 78 * $ adb shell am start -a com.android.email.FORCE_CREATE_ACCOUNT \ 79 * -e EMAIL test_account (at) gmail.com \ 80 * -e USER "Test Account Name" \ 81 * -e INCOMING imap+ssl+://test_account:test_password (at) imap.gmail.com \ 82 * -e OUTGOING smtp+ssl+://test_account:test_password (at) smtp.gmail.com 83 * 84 * Note: For accounts that require the full email address in the login, encode the @ as %40. 85 * Note: Exchange accounts that require device security policies cannot be created 86 * automatically. 87 * 88 * For accounts that correspond to services in providers.xml you can also use the following form 89 * 90 * $adb shell am start -a com.android.email.FORCE_CREATE_ACCOUNT \ 91 * -e EMAIL test_account (at) gmail.com \ 92 * -e PASSWORD test_password 93 * 94 * and the appropriate incoming/outgoing information will be filled in automatically. 95 */ 96 private static String INTENT_FORCE_CREATE_ACCOUNT; 97 private static final String EXTRA_FLOW_MODE = "FLOW_MODE"; 98 private static final String EXTRA_FLOW_ACCOUNT_TYPE = "FLOW_ACCOUNT_TYPE"; 99 private static final String EXTRA_CREATE_ACCOUNT_EMAIL = "EMAIL"; 100 private static final String EXTRA_CREATE_ACCOUNT_USER = "USER"; 101 private static final String EXTRA_CREATE_ACCOUNT_PASSWORD = "PASSWORD"; 102 private static final String EXTRA_CREATE_ACCOUNT_INCOMING = "INCOMING"; 103 private static final String EXTRA_CREATE_ACCOUNT_OUTGOING = "OUTGOING"; 104 private static final String EXTRA_CREATE_ACCOUNT_SYNC_LOOKBACK = "SYNC_LOOKBACK"; 105 106 private static final String CREATE_ACCOUNT_SYNC_ALL_VALUE = "ALL"; 107 108 private static final Boolean DEBUG_ALLOW_NON_TEST_HARNESS_CREATION = false; 109 110 protected static final String ACTION_JUMP_TO_INCOMING = "jumpToIncoming"; 111 protected static final String ACTION_JUMP_TO_OUTGOING = "jumpToOutgoing"; 112 protected static final String ACTION_JUMP_TO_OPTIONS = "jumpToOptions"; 113 114 private static final String SAVESTATE_KEY_IS_PROCESSING = "AccountSetupFinal.is_processing"; 115 private static final String SAVESTATE_KEY_STATE = "AccountSetupFinal.state"; 116 private static final String SAVESTATE_KEY_PROVIDER = "AccountSetupFinal.provider"; 117 private static final String SAVESTATE_KEY_AUTHENTICATOR_RESPONSE = "AccountSetupFinal.authResp"; 118 private static final String SAVESTATE_KEY_REPORT_AUTHENTICATOR_ERROR = 119 "AccountSetupFinal.authErr"; 120 private static final String SAVESTATE_KEY_IS_PRE_CONFIGURED = "AccountSetupFinal.preconfig"; 121 private static final String SAVESTATE_KEY_SKIP_AUTO_DISCOVER = "AccountSetupFinal.noAuto"; 122 private static final String SAVESTATE_KEY_PASSWORD_FAILED = "AccountSetupFinal.passwordFailed"; 123 124 private static final String CONTENT_FRAGMENT_TAG = "AccountSetupContentFragment"; 125 private static final String CREDENTIALS_BACKSTACK_TAG = "AccountSetupCredentialsFragment"; 126 127 // Collecting initial email and password 128 private static final int STATE_BASICS = 0; 129 // Show the user some interstitial message after email entry 130 private static final int STATE_BASICS_POST = 1; 131 // Account is not pre-configured, query user for account type 132 private static final int STATE_TYPE = 2; 133 // Account is pre-configured, but the user picked a different protocol 134 private static final int STATE_AB = 3; 135 // Collect initial password or oauth token 136 private static final int STATE_CREDENTIALS = 4; 137 // Account is a pre-configured account, run the checker 138 private static final int STATE_CHECKING_PRECONFIGURED = 5; 139 // Auto-discovering exchange account info, possibly other protocols later 140 private static final int STATE_AUTO_DISCOVER = 6; 141 // User is entering incoming settings 142 private static final int STATE_MANUAL_INCOMING = 7; 143 // We're checking incoming settings 144 private static final int STATE_CHECKING_INCOMING = 8; 145 // User is entering outgoing settings 146 private static final int STATE_MANUAL_OUTGOING = 9; 147 // We're checking outgoing settings 148 private static final int STATE_CHECKING_OUTGOING = 10; 149 // User is entering sync options 150 private static final int STATE_OPTIONS = 11; 151 // We're creating the account 152 private static final int STATE_CREATING = 12; 153 // User is entering account name and real name 154 private static final int STATE_NAMES = 13; 155 // we're finalizing the account 156 private static final int STATE_FINALIZE = 14; 157 158 private int mState = STATE_BASICS; 159 160 private boolean mIsProcessing = false; 161 private boolean mForceCreate = false; 162 private boolean mReportAccountAuthenticatorError; 163 private AccountAuthenticatorResponse mAccountAuthenticatorResponse; 164 // True if this provider is found in our providers.xml, set after Basics 165 private boolean mIsPreConfiguredProvider = false; 166 // True if the user selected manual setup 167 private boolean mSkipAutoDiscover = false; 168 // True if validating the pre-configured provider failed and we want manual setup 169 private boolean mPreConfiguredFailed = false; 170 171 private VendorPolicyLoader.Provider mProvider; 172 private boolean mPasswordFailed; 173 174 private static final int OWNER_NAME_LOADER_ID = 0; 175 private String mOwnerName; 176 177 private static final int EXISTING_ACCOUNTS_LOADER_ID = 1; 178 private Map<String, String> mExistingAccountsMap; 179 180 public static Intent actionNewAccountIntent(final Context context) { 181 final Intent i = new Intent(context, AccountSetupFinal.class); 182 i.putExtra(EXTRA_FLOW_MODE, SetupDataFragment.FLOW_MODE_NORMAL); 183 return i; 184 } 185 186 public static Intent actionNewAccountWithResultIntent(final Context context) { 187 final Intent i = new Intent(context, AccountSetupFinal.class); 188 i.putExtra(EXTRA_FLOW_MODE, SetupDataFragment.FLOW_MODE_NO_ACCOUNTS); 189 return i; 190 } 191 192 public static Intent actionGetCreateAccountIntent(final Context context, 193 final String accountManagerType) { 194 final Intent i = new Intent(context, AccountSetupFinal.class); 195 i.putExtra(EXTRA_FLOW_MODE, SetupDataFragment.FLOW_MODE_ACCOUNT_MANAGER); 196 i.putExtra(EXTRA_FLOW_ACCOUNT_TYPE, accountManagerType); 197 return i; 198 } 199 200 @Override 201 public void onCreate(Bundle savedInstanceState) { 202 super.onCreate(savedInstanceState); 203 204 final Intent intent = getIntent(); 205 final String action = intent.getAction(); 206 207 if (INTENT_FORCE_CREATE_ACCOUNT == null) { 208 INTENT_FORCE_CREATE_ACCOUNT = getString(R.string.intent_force_create_email_account); 209 } 210 211 setContentView(R.layout.account_setup_activity); 212 213 if (savedInstanceState != null) { 214 mIsProcessing = savedInstanceState.getBoolean(SAVESTATE_KEY_IS_PROCESSING, false); 215 mState = savedInstanceState.getInt(SAVESTATE_KEY_STATE, STATE_OPTIONS); 216 mProvider = (VendorPolicyLoader.Provider) 217 savedInstanceState.getSerializable(SAVESTATE_KEY_PROVIDER); 218 mAccountAuthenticatorResponse = 219 savedInstanceState.getParcelable(SAVESTATE_KEY_AUTHENTICATOR_RESPONSE); 220 mReportAccountAuthenticatorError = 221 savedInstanceState.getBoolean(SAVESTATE_KEY_REPORT_AUTHENTICATOR_ERROR); 222 mIsPreConfiguredProvider = 223 savedInstanceState.getBoolean(SAVESTATE_KEY_IS_PRE_CONFIGURED); 224 mSkipAutoDiscover = savedInstanceState.getBoolean(SAVESTATE_KEY_SKIP_AUTO_DISCOVER); 225 mPasswordFailed = savedInstanceState.getBoolean(SAVESTATE_KEY_PASSWORD_FAILED); 226 } else { 227 // If we're not restoring from a previous state, we want to configure the initial screen 228 229 // Set aside incoming AccountAuthenticatorResponse, if there was any 230 mAccountAuthenticatorResponse = getIntent() 231 .getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE); 232 if (mAccountAuthenticatorResponse != null) { 233 // When this Activity is called as part of account authentification flow, 234 // we are responsible for eventually reporting the result (success or failure) to 235 // the account manager. Most exit paths represent an failed or abandoned setup, 236 // so the default is to report the error. Success will be reported by the code in 237 // AccountSetupOptions that commits the finally created account. 238 mReportAccountAuthenticatorError = true; 239 } 240 241 // Initialize the SetupDataFragment 242 if (INTENT_FORCE_CREATE_ACCOUNT.equals(action)) { 243 mSetupData.setFlowMode(SetupDataFragment.FLOW_MODE_FORCE_CREATE); 244 } else { 245 final int intentFlowMode = intent.getIntExtra(EXTRA_FLOW_MODE, 246 SetupDataFragment.FLOW_MODE_UNSPECIFIED); 247 final String flowAccountType = intent.getStringExtra(EXTRA_FLOW_ACCOUNT_TYPE); 248 mSetupData.setAmProtocol( 249 EmailServiceUtils.getProtocolFromAccountType(this, flowAccountType)); 250 mSetupData.setFlowMode(intentFlowMode); 251 } 252 253 mState = STATE_BASICS; 254 // Support unit testing individual screens 255 if (TextUtils.equals(ACTION_JUMP_TO_INCOMING, action)) { 256 mState = STATE_MANUAL_INCOMING; 257 } else if (TextUtils.equals(ACTION_JUMP_TO_OUTGOING, action)) { 258 mState = STATE_MANUAL_OUTGOING; 259 } else if (TextUtils.equals(ACTION_JUMP_TO_OPTIONS, action)) { 260 mState = STATE_OPTIONS; 261 } 262 updateContentFragment(false /* addToBackstack */); 263 mPasswordFailed = false; 264 } 265 266 if (!mIsProcessing 267 && mSetupData.getFlowMode() == SetupDataFragment.FLOW_MODE_FORCE_CREATE) { 268 /** 269 * To support continuous testing, we allow the forced creation of accounts. 270 * This works in a manner fairly similar to automatic setup, in which the complete 271 * server Uri's are available, except that we will also skip checking (as if both 272 * checks were true) and all other UI. 273 * 274 * email: The email address for the new account 275 * user: The user name for the new account 276 * incoming: The URI-style string defining the incoming account 277 * outgoing: The URI-style string defining the outgoing account 278 */ 279 final String email = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_EMAIL); 280 final String user = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_USER); 281 final String password = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_PASSWORD); 282 final String incoming = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_INCOMING); 283 final String outgoing = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_OUTGOING); 284 final String syncLookbackText = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_SYNC_LOOKBACK); 285 final int syncLookback; 286 if (TextUtils.equals(syncLookbackText, CREATE_ACCOUNT_SYNC_ALL_VALUE)) { 287 syncLookback = SyncWindow.SYNC_WINDOW_ALL; 288 } else { 289 syncLookback = -1; 290 } 291 // If we've been explicitly provided with all the details to fill in the account, we 292 // can use them 293 final boolean explicitForm = !(TextUtils.isEmpty(user) || 294 TextUtils.isEmpty(incoming) || TextUtils.isEmpty(outgoing)); 295 // If we haven't been provided the details, but we have the password, we can look up 296 // the info from providers.xml 297 final boolean implicitForm = !TextUtils.isEmpty(password) && !explicitForm; 298 if (TextUtils.isEmpty(email) || !(explicitForm || implicitForm)) { 299 LogUtils.e(LogUtils.TAG, "Force account create requires extras EMAIL, " + 300 "USER, INCOMING, OUTGOING, or EMAIL and PASSWORD"); 301 finish(); 302 return; 303 } 304 305 if (implicitForm) { 306 final String[] emailParts = email.split("@"); 307 final String domain = emailParts[1].trim(); 308 mProvider = AccountSettingsUtils.findProviderForDomain(this, domain); 309 if (mProvider == null) { 310 LogUtils.e(LogUtils.TAG, "findProviderForDomain couldn't find provider"); 311 finish(); 312 return; 313 } 314 mIsPreConfiguredProvider = true; 315 mSetupData.setEmail(email); 316 boolean autoSetupCompleted = finishAutoSetup(); 317 if (!autoSetupCompleted) { 318 LogUtils.e(LogUtils.TAG, "Force create account failed to create account"); 319 finish(); 320 return; 321 } 322 final Account account = mSetupData.getAccount(); 323 final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this); 324 recvAuth.mPassword = password; 325 final HostAuth sendAuth = account.getOrCreateHostAuthSend(this); 326 sendAuth.mPassword = password; 327 } else { 328 final Account account = mSetupData.getAccount(); 329 330 try { 331 final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this); 332 recvAuth.setHostAuthFromString(incoming); 333 334 final HostAuth sendAuth = account.getOrCreateHostAuthSend(this); 335 sendAuth.setHostAuthFromString(outgoing); 336 } catch (URISyntaxException e) { 337 // If we can't set up the URL, don't continue 338 Toast.makeText(this, R.string.account_setup_username_password_toast, 339 Toast.LENGTH_LONG) 340 .show(); 341 finish(); 342 return; 343 } 344 345 populateSetupData(user, email); 346 // We need to do this after calling populateSetupData(), because that will 347 // overwrite it with the default values. 348 if (syncLookback >= SyncWindow.SYNC_WINDOW_ACCOUNT && 349 syncLookback <= SyncWindow.SYNC_WINDOW_ALL) { 350 account.mSyncLookback = syncLookback; 351 } 352 } 353 354 mState = STATE_OPTIONS; 355 updateContentFragment(false /* addToBackstack */); 356 getFragmentManager().executePendingTransactions(); 357 358 if (!DEBUG_ALLOW_NON_TEST_HARNESS_CREATION && 359 !ActivityManager.isRunningInTestHarness()) { 360 LogUtils.e(LogUtils.TAG, 361 "ERROR: Force account create only allowed while in test harness"); 362 finish(); 363 return; 364 } 365 366 mForceCreate = true; 367 } 368 369 // Launch a loader to look up the owner name. It should be ready well in advance of 370 // the time the user clicks next or manual. 371 getLoaderManager().initLoader(OWNER_NAME_LOADER_ID, null, 372 new LoaderManager.LoaderCallbacks<Cursor>() { 373 @Override 374 public Loader<Cursor> onCreateLoader(final int id, final Bundle args) { 375 return new CursorLoader(AccountSetupFinal.this, 376 ContactsContract.Profile.CONTENT_URI, 377 new String[] {ContactsContract.Profile.DISPLAY_NAME_PRIMARY}, 378 null, null, null); 379 } 380 381 @Override 382 public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) { 383 if (data != null && data.moveToFirst()) { 384 mOwnerName = data.getString(data.getColumnIndex( 385 ContactsContract.Profile.DISPLAY_NAME_PRIMARY)); 386 } 387 } 388 389 @Override 390 public void onLoaderReset(final Loader<Cursor> loader) {} 391 }); 392 393 // Launch a loader to cache some info about existing accounts so we can dupe-check against 394 // them. 395 getLoaderManager().initLoader(EXISTING_ACCOUNTS_LOADER_ID, null, 396 new LoaderManager.LoaderCallbacks<Cursor> () { 397 @Override 398 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 399 return new CursorLoader(AccountSetupFinal.this, Account.CONTENT_URI, 400 new String[] {AccountColumns.EMAIL_ADDRESS, 401 AccountColumns.DISPLAY_NAME}, 402 null, null, null); 403 } 404 405 @Override 406 public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 407 if (data == null) { 408 mExistingAccountsMap = null; 409 return; 410 } 411 412 mExistingAccountsMap = new HashMap<String, String>(); 413 414 final int emailColumnIndex = data.getColumnIndex( 415 AccountColumns.EMAIL_ADDRESS); 416 final int displayNameColumnIndex = 417 data.getColumnIndex(AccountColumns.DISPLAY_NAME); 418 419 while (data.moveToNext()) { 420 final String email = data.getString(emailColumnIndex); 421 final String displayName = data.getString(displayNameColumnIndex); 422 mExistingAccountsMap.put(email, 423 TextUtils.isEmpty(displayName) ? email : displayName); 424 } 425 } 426 427 @Override 428 public void onLoaderReset(Loader<Cursor> loader) { 429 mExistingAccountsMap = null; 430 } 431 }); 432 } 433 434 @Override 435 protected void onResume() { 436 super.onResume(); 437 if (mForceCreate) { 438 mForceCreate = false; 439 440 // We need to do this after onCreate so that we can ensure that the fragment is 441 // fully created before querying it. 442 // This will call initiateAccountCreation() for us 443 proceed(); 444 } 445 } 446 447 @Override 448 public void onSaveInstanceState(Bundle outState) { 449 super.onSaveInstanceState(outState); 450 outState.putBoolean(SAVESTATE_KEY_IS_PROCESSING, mIsProcessing); 451 outState.putInt(SAVESTATE_KEY_STATE, mState); 452 outState.putSerializable(SAVESTATE_KEY_PROVIDER, mProvider); 453 outState.putParcelable(SAVESTATE_KEY_AUTHENTICATOR_RESPONSE, mAccountAuthenticatorResponse); 454 outState.putBoolean(SAVESTATE_KEY_REPORT_AUTHENTICATOR_ERROR, 455 mReportAccountAuthenticatorError); 456 outState.putBoolean(SAVESTATE_KEY_IS_PRE_CONFIGURED, mIsPreConfiguredProvider); 457 outState.putBoolean(SAVESTATE_KEY_PASSWORD_FAILED, mPasswordFailed); 458 } 459 460 /** 461 * Swap in the new fragment according to mState. This pushes the current fragment onto the back 462 * stack, so only call it once per transition. 463 */ 464 private void updateContentFragment(boolean addToBackstack) { 465 final AccountSetupFragment f; 466 String backstackTag = null; 467 468 switch (mState) { 469 case STATE_BASICS: 470 f = AccountSetupBasicsFragment.newInstance(); 471 break; 472 case STATE_TYPE: 473 f = AccountSetupTypeFragment.newInstance(); 474 break; 475 case STATE_AB: 476 f = AccountSetupABFragment.newInstance(mSetupData.getEmail(), 477 mSetupData.getAmProtocol(), mSetupData.getIncomingProtocol(this)); 478 break; 479 case STATE_CREDENTIALS: 480 f = AccountSetupCredentialsFragment.newInstance(mSetupData.getEmail(), 481 mSetupData.getIncomingProtocol(this), mSetupData.getClientCert(this), 482 mPasswordFailed, false /* standalone */); 483 backstackTag = CREDENTIALS_BACKSTACK_TAG; 484 break; 485 case STATE_MANUAL_INCOMING: 486 f = AccountSetupIncomingFragment.newInstance(false); 487 break; 488 case STATE_MANUAL_OUTGOING: 489 f = AccountSetupOutgoingFragment.newInstance(false); 490 break; 491 case STATE_OPTIONS: 492 f = AccountSetupOptionsFragment.newInstance(); 493 break; 494 case STATE_NAMES: 495 f = AccountSetupNamesFragment.newInstance(); 496 break; 497 default: 498 throw new IllegalStateException("Incorrect state " + mState); 499 } 500 f.setState(mState); 501 final FragmentTransaction ft = getFragmentManager().beginTransaction(); 502 ft.setCustomAnimations(R.anim.fade_in, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out); 503 ft.replace(R.id.setup_fragment_container, f, CONTENT_FRAGMENT_TAG); 504 if (addToBackstack) { 505 ft.addToBackStack(backstackTag); 506 } 507 ft.commit(); 508 509 final InputMethodManager imm = 510 (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 511 final View fragment_container = findViewById(R.id.setup_fragment_container); 512 imm.hideSoftInputFromWindow(fragment_container.getWindowToken(), 513 0 /* flags: always hide */); 514 } 515 516 /** 517 * Retrieve the current content fragment 518 * @return The content fragment or null if it wasn't found for some reason 519 */ 520 private AccountSetupFragment getContentFragment() { 521 return (AccountSetupFragment) getFragmentManager().findFragmentByTag(CONTENT_FRAGMENT_TAG); 522 } 523 524 /** 525 * Reads the flow state saved into the current fragment and restores mState to it, also 526 * resetting the headline at the same time. 527 */ 528 private void resetStateFromCurrentFragment() { 529 AccountSetupFragment f = getContentFragment(); 530 mState = f.getState(); 531 } 532 533 /** 534 * Main choreography function to handle moving forward through scenes. Moving back should be 535 * generally handled for us by the back stack 536 */ 537 protected void proceed() { 538 mIsProcessing = false; 539 final AccountSetupFragment oldContentFragment = getContentFragment(); 540 if (oldContentFragment != null) { 541 oldContentFragment.setNextButtonEnabled(true); 542 } 543 544 getFragmentManager().executePendingTransactions(); 545 546 switch (mState) { 547 case STATE_BASICS: 548 final boolean advance = onBasicsComplete(); 549 if (!advance) { 550 mState = STATE_BASICS_POST; 551 break; 552 } // else fall through 553 case STATE_BASICS_POST: 554 if (shouldDivertToManual()) { 555 mSkipAutoDiscover = true; 556 mIsPreConfiguredProvider = false; 557 mState = STATE_TYPE; 558 } else { 559 mSkipAutoDiscover = false; 560 if (mIsPreConfiguredProvider) { 561 if (!TextUtils.isEmpty(mSetupData.getAmProtocol()) && 562 !TextUtils.equals(mSetupData.getAmProtocol(), 563 mSetupData.getIncomingProtocol(this))) { 564 mState = STATE_AB; 565 } else { 566 mState = STATE_CREDENTIALS; 567 if (possiblyDivertToGmail()) { 568 return; 569 } 570 } 571 } else { 572 final String amProtocol = mSetupData.getAmProtocol(); 573 if (!TextUtils.isEmpty(amProtocol)) { 574 mSetupData.setIncomingProtocol(this, amProtocol); 575 final Account account = mSetupData.getAccount(); 576 setDefaultsForProtocol(account); 577 mState = STATE_CREDENTIALS; 578 } else { 579 mState = STATE_TYPE; 580 } 581 } 582 } 583 updateContentFragment(true /* addToBackstack */); 584 break; 585 case STATE_TYPE: 586 // We either got here through "Manual Setup" or because we didn't find the provider 587 mState = STATE_CREDENTIALS; 588 updateContentFragment(true /* addToBackstack */); 589 break; 590 case STATE_AB: 591 if (possiblyDivertToGmail()) { 592 return; 593 } 594 mState = STATE_CREDENTIALS; 595 updateContentFragment(true /* addToBackstack */); 596 break; 597 case STATE_CREDENTIALS: 598 collectCredentials(); 599 if (mIsPreConfiguredProvider) { 600 mState = STATE_CHECKING_PRECONFIGURED; 601 initiateCheckSettingsFragment(SetupDataFragment.CHECK_INCOMING 602 | SetupDataFragment.CHECK_OUTGOING); 603 } else { 604 populateHostAuthsFromSetupData(); 605 if (mSkipAutoDiscover) { 606 mState = STATE_MANUAL_INCOMING; 607 updateContentFragment(true /* addToBackstack */); 608 } else { 609 mState = STATE_AUTO_DISCOVER; 610 initiateAutoDiscover(); 611 } 612 } 613 break; 614 case STATE_CHECKING_PRECONFIGURED: 615 if (mPreConfiguredFailed) { 616 if (mPasswordFailed) { 617 // Get rid of the previous instance of the AccountSetupCredentialsFragment. 618 FragmentManager fm = getFragmentManager(); 619 fm.popBackStackImmediate(CREDENTIALS_BACKSTACK_TAG, 0); 620 final AccountSetupCredentialsFragment f = (AccountSetupCredentialsFragment) 621 getContentFragment(); 622 f.setPasswordFailed(mPasswordFailed); 623 resetStateFromCurrentFragment(); 624 } else { 625 mState = STATE_MANUAL_INCOMING; 626 updateContentFragment(true /* addToBackstack */); 627 } 628 } else { 629 mState = STATE_OPTIONS; 630 updateContentFragment(true /* addToBackstack */); 631 } 632 break; 633 case STATE_AUTO_DISCOVER: 634 // TODO: figure out if we can skip past manual setup 635 mState = STATE_MANUAL_INCOMING; 636 updateContentFragment(true); 637 break; 638 case STATE_MANUAL_INCOMING: 639 onIncomingComplete(); 640 mState = STATE_CHECKING_INCOMING; 641 initiateCheckSettingsFragment(SetupDataFragment.CHECK_INCOMING); 642 break; 643 case STATE_CHECKING_INCOMING: 644 final EmailServiceUtils.EmailServiceInfo serviceInfo = 645 mSetupData.getIncomingServiceInfo(this); 646 if (serviceInfo.usesSmtp) { 647 mState = STATE_MANUAL_OUTGOING; 648 } else { 649 mState = STATE_OPTIONS; 650 } 651 updateContentFragment(true /* addToBackstack */); 652 break; 653 case STATE_MANUAL_OUTGOING: 654 onOutgoingComplete(); 655 mState = STATE_CHECKING_OUTGOING; 656 initiateCheckSettingsFragment(SetupDataFragment.CHECK_OUTGOING); 657 break; 658 case STATE_CHECKING_OUTGOING: 659 mState = STATE_OPTIONS; 660 updateContentFragment(true /* addToBackstack */); 661 break; 662 case STATE_OPTIONS: 663 mState = STATE_CREATING; 664 initiateAccountCreation(); 665 break; 666 case STATE_CREATING: 667 mState = STATE_NAMES; 668 updateContentFragment(true /* addToBackstack */); 669 if (mSetupData.getFlowMode() == SetupDataFragment.FLOW_MODE_FORCE_CREATE) { 670 getFragmentManager().executePendingTransactions(); 671 initiateAccountFinalize(); 672 } 673 break; 674 case STATE_NAMES: 675 initiateAccountFinalize(); 676 break; 677 case STATE_FINALIZE: 678 finish(); 679 break; 680 default: 681 LogUtils.wtf(LogUtils.TAG, "Unknown state %d", mState); 682 break; 683 } 684 } 685 686 /** 687 * Check if we should divert to creating a Gmail account instead 688 * @return true if we diverted 689 */ 690 private boolean possiblyDivertToGmail() { 691 // TODO: actually divert here 692 final EmailServiceUtils.EmailServiceInfo info = 693 mSetupData.getIncomingServiceInfo(this); 694 if (TextUtils.equals(info.protocol, "gmail")) { 695 final Bundle options = new Bundle(1); 696 options.putBoolean("allowSkip", false); 697 AccountManager.get(this).addAccount("com.google", 698 "mail" /* authTokenType */, 699 null, 700 options, 701 this, null, null); 702 703 finish(); 704 return true; 705 } 706 return false; 707 } 708 709 /** 710 * Block the back key if we are currently processing the "next" key" 711 */ 712 @Override 713 public void onBackPressed() { 714 if (mIsProcessing) { 715 return; 716 } 717 if (mState == STATE_NAMES) { 718 finish(); 719 } else { 720 super.onBackPressed(); 721 } 722 // After super.onBackPressed() our fragment should be in place, so query the state we 723 // installed it for 724 resetStateFromCurrentFragment(); 725 } 726 727 @Override 728 public void setAccount(Account account) { 729 mSetupData.setAccount(account); 730 } 731 732 @Override 733 public void finish() { 734 // If the account manager initiated the creation, and success was not reported, 735 // then we assume that we're giving up (for any reason) - report failure. 736 if (mReportAccountAuthenticatorError) { 737 if (mAccountAuthenticatorResponse != null) { 738 mAccountAuthenticatorResponse 739 .onError(AccountManager.ERROR_CODE_CANCELED, "canceled"); 740 mAccountAuthenticatorResponse = null; 741 } 742 } 743 super.finish(); 744 } 745 746 @Override 747 public void onNextButton() { 748 // Some states are handled without UI, block double-presses here 749 if (!mIsProcessing) { 750 proceed(); 751 } 752 } 753 754 /** 755 * @return true to proceed, false to remain on the current screen 756 */ 757 private boolean onBasicsComplete() { 758 final AccountSetupBasicsFragment f = (AccountSetupBasicsFragment) getContentFragment(); 759 final String email = f.getEmail(); 760 761 // Reset the protocol choice in case the user has back-navigated here 762 mSetupData.setIncomingProtocol(this, null); 763 764 if (!TextUtils.equals(email, mSetupData.getEmail())) { 765 // If the user changes their email address, clear the password failed state 766 mPasswordFailed = false; 767 } 768 mSetupData.setEmail(email); 769 770 final String[] emailParts = email.split("@"); 771 final String domain = emailParts[1].trim(); 772 mProvider = AccountSettingsUtils.findProviderForDomain(this, domain); 773 if (mProvider != null) { 774 mIsPreConfiguredProvider = true; 775 if (mProvider.note != null) { 776 final AccountSetupNoteDialogFragment dialogFragment = 777 AccountSetupNoteDialogFragment.newInstance(mProvider.note); 778 dialogFragment.show(getFragmentManager(), AccountSetupNoteDialogFragment.TAG); 779 return false; 780 } else { 781 return finishAutoSetup(); 782 } 783 } else { 784 mIsPreConfiguredProvider = false; 785 final String existingAccountName = 786 mExistingAccountsMap != null ? mExistingAccountsMap.get(email) : null; 787 if (!TextUtils.isEmpty(existingAccountName)) { 788 showDuplicateAccountDialog(existingAccountName); 789 return false; 790 } else { 791 populateSetupData(mOwnerName, email); 792 mSkipAutoDiscover = false; 793 return true; 794 } 795 } 796 } 797 798 private void showDuplicateAccountDialog(final String existingAccountName) { 799 final DuplicateAccountDialogFragment dialogFragment = 800 DuplicateAccountDialogFragment.newInstance(existingAccountName); 801 dialogFragment.show(getFragmentManager(), DuplicateAccountDialogFragment.TAG); 802 } 803 804 @Override 805 public void onDuplicateAccountDialogDismiss() { 806 resetStateFromCurrentFragment(); 807 } 808 809 private boolean shouldDivertToManual() { 810 final AccountSetupBasicsFragment f = (AccountSetupBasicsFragment) getContentFragment(); 811 return f.isManualSetup(); 812 } 813 814 @Override 815 public void onCredentialsComplete(Bundle results) { 816 proceed(); 817 } 818 819 private void collectCredentials() { 820 final AccountSetupCredentialsFragment f = (AccountSetupCredentialsFragment) 821 getContentFragment(); 822 final Bundle results = f.getCredentialResults(); 823 mSetupData.setCredentialResults(results); 824 final Account account = mSetupData.getAccount(); 825 final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this); 826 AccountSetupCredentialsFragment.populateHostAuthWithResults(this, recvAuth, 827 mSetupData.getCredentialResults()); 828 mSetupData.setIncomingCredLoaded(true); 829 final EmailServiceUtils.EmailServiceInfo info = mSetupData.getIncomingServiceInfo(this); 830 if (info.usesSmtp) { 831 final HostAuth sendAuth = account.getOrCreateHostAuthSend(this); 832 AccountSetupCredentialsFragment.populateHostAuthWithResults(this, sendAuth, 833 mSetupData.getCredentialResults()); 834 mSetupData.setOutgoingCredLoaded(true); 835 } 836 } 837 838 @Override 839 public void onNoteDialogComplete() { 840 finishAutoSetup(); 841 proceed(); 842 } 843 844 @Override 845 public void onNoteDialogCancel() { 846 resetStateFromCurrentFragment(); 847 } 848 849 /** 850 * Finish the auto setup process, in some cases after showing a warning dialog. 851 * Happens after onBasicsComplete 852 * @return true to proceed, false to remain on the current screen 853 */ 854 private boolean finishAutoSetup() { 855 final String email = mSetupData.getEmail(); 856 857 try { 858 mProvider.expandTemplates(email); 859 860 final String primaryProtocol = HostAuth.getProtocolFromString(mProvider.incomingUri); 861 EmailServiceUtils.EmailServiceInfo info = 862 EmailServiceUtils.getServiceInfo(this, primaryProtocol); 863 // If the protocol isn't one we can use, and we're not diverting to gmail, try the alt 864 if (!info.isGmailStub && !EmailServiceUtils.isServiceAvailable(this, info.protocol)) { 865 LogUtils.d(LogUtils.TAG, "Protocol %s not available, using alternate", 866 info.protocol); 867 mProvider.expandAlternateTemplates(email); 868 final String alternateProtocol = HostAuth.getProtocolFromString( 869 mProvider.incomingUri); 870 info = EmailServiceUtils.getServiceInfo(this, alternateProtocol); 871 } 872 final Account account = mSetupData.getAccount(); 873 final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this); 874 recvAuth.setHostAuthFromString(mProvider.incomingUri); 875 876 recvAuth.setUserName(mProvider.incomingUsername); 877 recvAuth.mPort = 878 ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) ? info.portSsl : info.port; 879 880 if (info.usesSmtp) { 881 final HostAuth sendAuth = account.getOrCreateHostAuthSend(this); 882 sendAuth.setHostAuthFromString(mProvider.outgoingUri); 883 sendAuth.setUserName(mProvider.outgoingUsername); 884 } 885 886 // Populate the setup data, assuming that the duplicate account check will succeed 887 populateSetupData(mOwnerName, email); 888 889 final String duplicateAccountName = 890 mExistingAccountsMap != null ? mExistingAccountsMap.get(email) : null; 891 if (duplicateAccountName != null) { 892 showDuplicateAccountDialog(duplicateAccountName); 893 return false; 894 } 895 } catch (URISyntaxException e) { 896 mSkipAutoDiscover = false; 897 mPreConfiguredFailed = true; 898 } 899 return true; 900 } 901 902 903 /** 904 * Helper method to fill in some per-protocol defaults 905 * @param account Account object to fill in 906 */ 907 public void setDefaultsForProtocol(Account account) { 908 final EmailServiceUtils.EmailServiceInfo info = mSetupData.getIncomingServiceInfo(this); 909 if (info == null) return; 910 account.mSyncInterval = info.defaultSyncInterval; 911 account.mSyncLookback = info.defaultLookback; 912 if (info.offerLocalDeletes) { 913 account.setDeletePolicy(info.defaultLocalDeletes); 914 } 915 } 916 917 /** 918 * Populate SetupData's account with complete setup info, assumes that the receive auth is 919 * created and its protocol is set 920 */ 921 private void populateSetupData(String senderName, String senderEmail) { 922 final Account account = mSetupData.getAccount(); 923 account.setSenderName(senderName); 924 account.setEmailAddress(senderEmail); 925 account.setDisplayName(senderEmail); 926 setDefaultsForProtocol(account); 927 } 928 929 private void onIncomingComplete() { 930 AccountSetupIncomingFragment f = (AccountSetupIncomingFragment) getContentFragment(); 931 f.collectUserInput(); 932 } 933 934 private void onOutgoingComplete() { 935 AccountSetupOutgoingFragment f = (AccountSetupOutgoingFragment) getContentFragment(); 936 f.collectUserInput(); 937 } 938 939 // This callback method is only applicable to using Incoming/Outgoing fragments in settings mode 940 @Override 941 public void onAccountServerUIComplete(int checkMode) {} 942 943 // This callback method is only applicable to using Incoming/Outgoing fragments in settings mode 944 @Override 945 public void onAccountServerSaveComplete() {} 946 947 private void populateHostAuthsFromSetupData() { 948 final String email = mSetupData.getEmail(); 949 final String[] emailParts = email.split("@"); 950 final String domain = emailParts[1]; 951 952 final Account account = mSetupData.getAccount(); 953 954 final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this); 955 recvAuth.setUserName(email); 956 recvAuth.setConnection(mSetupData.getIncomingProtocol(), domain, 957 HostAuth.PORT_UNKNOWN, HostAuth.FLAG_NONE); 958 AccountSetupCredentialsFragment.populateHostAuthWithResults(this, recvAuth, 959 mSetupData.getCredentialResults()); 960 mSetupData.setIncomingCredLoaded(true); 961 962 final EmailServiceUtils.EmailServiceInfo info = 963 mSetupData.getIncomingServiceInfo(this); 964 if (info.usesSmtp) { 965 final HostAuth sendAuth = account.getOrCreateHostAuthSend(this); 966 sendAuth.setUserName(email); 967 sendAuth.setConnection(HostAuth.LEGACY_SCHEME_SMTP, domain, 968 HostAuth.PORT_UNKNOWN, HostAuth.FLAG_NONE); 969 AccountSetupCredentialsFragment.populateHostAuthWithResults(this, sendAuth, 970 mSetupData.getCredentialResults()); 971 mSetupData.setOutgoingCredLoaded(true); 972 } 973 } 974 975 private void initiateAutoDiscover() { 976 // Populate the setup data, assuming that the duplicate account check will succeed 977 initiateCheckSettingsFragment(SetupDataFragment.CHECK_AUTODISCOVER); 978 } 979 980 private void initiateCheckSettingsFragment(int checkMode) { 981 final Fragment f = AccountCheckSettingsFragment.newInstance(checkMode); 982 final Fragment d = CheckSettingsProgressDialogFragment.newInstance(checkMode); 983 getFragmentManager().beginTransaction() 984 .add(f, AccountCheckSettingsFragment.TAG) 985 .add(d, CheckSettingsProgressDialogFragment.TAG) 986 .commit(); 987 } 988 989 @Override 990 public void onCheckSettingsProgressDialogCancel() { 991 dismissCheckSettingsFragment(); 992 resetStateFromCurrentFragment(); 993 } 994 995 private void dismissCheckSettingsFragment() { 996 final Fragment f = getFragmentManager().findFragmentByTag(AccountCheckSettingsFragment.TAG); 997 final Fragment d = 998 getFragmentManager().findFragmentByTag(CheckSettingsProgressDialogFragment.TAG); 999 getFragmentManager().beginTransaction() 1000 .remove(f) 1001 .remove(d) 1002 .commit(); 1003 } 1004 1005 @Override 1006 public void onCheckSettingsError(int reason, String message) { 1007 if (reason == CheckSettingsErrorDialogFragment.REASON_AUTHENTICATION_FAILED || 1008 reason == CheckSettingsErrorDialogFragment.REASON_CERTIFICATE_REQUIRED) { 1009 // TODO: possibly split password and cert error conditions 1010 mPasswordFailed = true; 1011 } 1012 dismissCheckSettingsFragment(); 1013 final DialogFragment f = 1014 CheckSettingsErrorDialogFragment.newInstance(reason, message); 1015 f.show(getFragmentManager(), CheckSettingsErrorDialogFragment.TAG); 1016 } 1017 1018 @Override 1019 public void onCheckSettingsErrorDialogEditCertificate() { 1020 if (mState == STATE_CHECKING_PRECONFIGURED) { 1021 mPreConfiguredFailed = true; 1022 proceed(); 1023 } else { 1024 resetStateFromCurrentFragment(); 1025 } 1026 final AccountSetupIncomingFragment f = (AccountSetupIncomingFragment) getContentFragment(); 1027 f.onCertificateRequested(); 1028 } 1029 1030 @Override 1031 public void onCheckSettingsErrorDialogEditSettings() { 1032 // If we're checking pre-configured, set a flag that we failed and navigate forwards to 1033 // incoming settings 1034 if (mState == STATE_CHECKING_PRECONFIGURED || mState == STATE_AUTO_DISCOVER) { 1035 mPreConfiguredFailed = true; 1036 proceed(); 1037 } else { 1038 resetStateFromCurrentFragment(); 1039 } 1040 } 1041 1042 @Override 1043 public void onCheckSettingsComplete() { 1044 mPreConfiguredFailed = false; 1045 mPasswordFailed = false; 1046 dismissCheckSettingsFragment(); 1047 proceed(); 1048 } 1049 1050 @Override 1051 public void onCheckSettingsAutoDiscoverComplete(int result) { 1052 dismissCheckSettingsFragment(); 1053 proceed(); 1054 } 1055 1056 @Override 1057 public void onCheckSettingsSecurityRequired(String hostName) { 1058 dismissCheckSettingsFragment(); 1059 final DialogFragment f = SecurityRequiredDialogFragment.newInstance(hostName); 1060 f.show(getFragmentManager(), SecurityRequiredDialogFragment.TAG); 1061 } 1062 1063 @Override 1064 public void onSecurityRequiredDialogResult(boolean ok) { 1065 if (ok) { 1066 proceed(); 1067 } else { 1068 resetStateFromCurrentFragment(); 1069 } 1070 } 1071 1072 @Override 1073 public void onChooseProtocol(String protocol) { 1074 mSetupData.setIncomingProtocol(this, protocol); 1075 final Account account = mSetupData.getAccount(); 1076 setDefaultsForProtocol(account); 1077 proceed(); 1078 } 1079 1080 @Override 1081 public void onABProtocolDisambiguated(String chosenProtocol) { 1082 if (!TextUtils.equals(mSetupData.getIncomingProtocol(this), chosenProtocol)) { 1083 mIsPreConfiguredProvider = false; 1084 mSetupData.setIncomingProtocol(this, chosenProtocol); 1085 final Account account = mSetupData.getAccount(); 1086 setDefaultsForProtocol(account); 1087 } 1088 proceed(); 1089 } 1090 1091 /** 1092 * Ths is called when the user clicks the "done" button. 1093 * It collects the data from the UI, updates the setup account record, and launches a fragment 1094 * which handles creating the account in the system and database. 1095 */ 1096 private void initiateAccountCreation() { 1097 mIsProcessing = true; 1098 getContentFragment().setNextButtonEnabled(false); 1099 1100 final Account account = mSetupData.getAccount(); 1101 if (account.mHostAuthRecv == null) { 1102 throw new IllegalStateException("in AccountSetupOptions with null mHostAuthRecv"); 1103 } 1104 1105 final AccountSetupOptionsFragment fragment = (AccountSetupOptionsFragment) 1106 getContentFragment(); 1107 if (fragment == null) { 1108 throw new IllegalStateException("Fragment missing!"); 1109 } 1110 1111 account.setDisplayName(account.getEmailAddress()); 1112 int newFlags = account.getFlags() & ~(Account.FLAGS_BACKGROUND_ATTACHMENTS); 1113 final EmailServiceUtils.EmailServiceInfo serviceInfo = 1114 mSetupData.getIncomingServiceInfo(this); 1115 if (serviceInfo.offerAttachmentPreload && fragment.getBackgroundAttachmentsValue()) { 1116 newFlags |= Account.FLAGS_BACKGROUND_ATTACHMENTS; 1117 } 1118 final HostAuth hostAuth = account.getOrCreateHostAuthRecv(this); 1119 if (hostAuth.mProtocol.equals(getString(R.string.protocol_eas))) { 1120 try { 1121 final double protocolVersionDouble = Double.parseDouble(account.mProtocolVersion); 1122 if (protocolVersionDouble >= 12.0) { 1123 // If the the account is EAS and the protocol version is above 12.0, 1124 // we know that SmartForward is enabled and the various search flags 1125 // should be enabled first. 1126 // TODO: Move this into protocol specific code in the future. 1127 newFlags |= Account.FLAGS_SUPPORTS_SMART_FORWARD | 1128 Account.FLAGS_SUPPORTS_GLOBAL_SEARCH | Account.FLAGS_SUPPORTS_SEARCH; 1129 } 1130 } catch (NumberFormatException e) { 1131 LogUtils.wtf(LogUtils.TAG, e, "Exception thrown parsing the protocol version."); 1132 } 1133 } 1134 account.setFlags(newFlags); 1135 account.setSyncInterval(fragment.getCheckFrequencyValue()); 1136 final Integer syncWindowValue = fragment.getAccountSyncWindowValue(); 1137 if (syncWindowValue != null) { 1138 account.setSyncLookback(syncWindowValue); 1139 } 1140 1141 // Finish setting up the account, and commit it to the database 1142 if (mSetupData.getPolicy() != null) { 1143 account.mFlags |= Account.FLAGS_SECURITY_HOLD; 1144 account.mPolicy = mSetupData.getPolicy(); 1145 } 1146 1147 // Finally, write the completed account (for the first time) and then 1148 // install it into the Account manager as well. These are done off-thread. 1149 // The account manager will report back via the callback, which will take us to 1150 // the next operations. 1151 final boolean syncEmail = fragment.getSyncEmailValue(); 1152 final boolean syncCalendar = serviceInfo.syncCalendar && fragment.getSyncCalendarValue(); 1153 final boolean syncContacts = serviceInfo.syncContacts && fragment.getSyncContactsValue(); 1154 final boolean enableNotifications = fragment.getNotifyValue(); 1155 1156 final Fragment f = AccountCreationFragment.newInstance(account, syncEmail, syncCalendar, 1157 syncContacts, enableNotifications); 1158 final FragmentTransaction ft = getFragmentManager().beginTransaction(); 1159 ft.add(f, AccountCreationFragment.TAG); 1160 ft.commit(); 1161 1162 showCreateAccountDialog(); 1163 } 1164 1165 /** 1166 * Called by the account creation fragment after it has completed. 1167 * We do a small amount of work here before moving on to the next state. 1168 */ 1169 @Override 1170 public void onAccountCreationFragmentComplete() { 1171 destroyAccountCreationFragment(); 1172 // If the account manager initiated the creation, and success was not reported, 1173 // then we assume that we're giving up (for any reason) - report failure. 1174 if (mAccountAuthenticatorResponse != null) { 1175 final EmailServiceUtils.EmailServiceInfo info = mSetupData.getIncomingServiceInfo(this); 1176 final Bundle b = new Bundle(2); 1177 b.putString(AccountManager.KEY_ACCOUNT_NAME, mSetupData.getEmail()); 1178 b.putString(AccountManager.KEY_ACCOUNT_TYPE, info.accountType); 1179 mAccountAuthenticatorResponse.onResult(b); 1180 mAccountAuthenticatorResponse = null; 1181 mReportAccountAuthenticatorError = false; 1182 } 1183 setResult(RESULT_OK); 1184 proceed(); 1185 } 1186 1187 @Override 1188 public void destroyAccountCreationFragment() { 1189 dismissCreateAccountDialog(); 1190 1191 final Fragment f = getFragmentManager().findFragmentByTag(AccountCreationFragment.TAG); 1192 if (f == null) { 1193 LogUtils.wtf(LogUtils.TAG, "Couldn't find AccountCreationFragment to destroy"); 1194 } 1195 getFragmentManager().beginTransaction() 1196 .remove(f) 1197 .commit(); 1198 } 1199 1200 1201 public static class CreateAccountDialogFragment extends DialogFragment { 1202 public static final String TAG = "CreateAccountDialogFragment"; 1203 public CreateAccountDialogFragment() {} 1204 1205 public static CreateAccountDialogFragment newInstance() { 1206 return new CreateAccountDialogFragment(); 1207 } 1208 1209 @Override 1210 public Dialog onCreateDialog(Bundle savedInstanceState) { 1211 /// Show "Creating account..." dialog 1212 setCancelable(false); 1213 final ProgressDialog d = new ProgressDialog(getActivity()); 1214 d.setIndeterminate(true); 1215 d.setMessage(getString(R.string.account_setup_creating_account_msg)); 1216 return d; 1217 } 1218 } 1219 1220 protected void showCreateAccountDialog() { 1221 CreateAccountDialogFragment.newInstance() 1222 .show(getFragmentManager(), CreateAccountDialogFragment.TAG); 1223 } 1224 1225 protected void dismissCreateAccountDialog() { 1226 final DialogFragment f = (DialogFragment) 1227 getFragmentManager().findFragmentByTag(CreateAccountDialogFragment.TAG); 1228 if (f != null) { 1229 f.dismiss(); 1230 } 1231 } 1232 1233 public static class CreateAccountErrorDialogFragment extends DialogFragment 1234 implements DialogInterface.OnClickListener { 1235 public CreateAccountErrorDialogFragment() {} 1236 1237 @Override 1238 public Dialog onCreateDialog(Bundle savedInstanceState) { 1239 final String message = getString(R.string.account_setup_failed_dlg_auth_message, 1240 R.string.system_account_create_failed); 1241 1242 setCancelable(false); 1243 return new AlertDialog.Builder(getActivity()) 1244 .setIconAttribute(android.R.attr.alertDialogIcon) 1245 .setTitle(R.string.account_setup_failed_dlg_title) 1246 .setMessage(message) 1247 .setPositiveButton(android.R.string.ok, this) 1248 .create(); 1249 } 1250 1251 @Override 1252 public void onClick(DialogInterface dialog, int which) { 1253 getActivity().finish(); 1254 } 1255 } 1256 1257 /** 1258 * This is called if MailService.setupAccountManagerAccount() fails for some reason 1259 */ 1260 @Override 1261 public void showCreateAccountErrorDialog() { 1262 new CreateAccountErrorDialogFragment().show(getFragmentManager(), null); 1263 } 1264 1265 /** 1266 * Collect the data from AccountSetupNames and finish up account creation 1267 */ 1268 private void initiateAccountFinalize() { 1269 mIsProcessing = true; 1270 getContentFragment().setNextButtonEnabled(false); 1271 1272 AccountSetupNamesFragment fragment = (AccountSetupNamesFragment) getContentFragment(); 1273 // Update account object from UI 1274 final Account account = mSetupData.getAccount(); 1275 final String description = fragment.getDescription(); 1276 if (!TextUtils.isEmpty(description)) { 1277 account.setDisplayName(description); 1278 } 1279 account.setSenderName(fragment.getSenderName()); 1280 1281 final Fragment f = AccountFinalizeFragment.newInstance(account); 1282 final FragmentTransaction ft = getFragmentManager().beginTransaction(); 1283 ft.add(f, AccountFinalizeFragment.TAG); 1284 ft.commit(); 1285 } 1286 1287 /** 1288 * Called when the AccountFinalizeFragment has finished its tasks 1289 */ 1290 @Override 1291 public void onAccountFinalizeFragmentComplete() { 1292 finish(); 1293 } 1294 } 1295