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