summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/email/activity/setup/AccountCheckSettingsFragment.java616
-rw-r--r--src/com/android/email/activity/setup/AccountCreationFragment.java32
-rw-r--r--src/com/android/email/activity/setup/AccountCredentials.java77
-rw-r--r--src/com/android/email/activity/setup/AccountFinalizeFragment.java134
-rw-r--r--src/com/android/email/activity/setup/AccountServerBaseFragment.java208
-rw-r--r--src/com/android/email/activity/setup/AccountSettings.java170
-rw-r--r--src/com/android/email/activity/setup/AccountSetupActivity.java14
-rw-r--r--src/com/android/email/activity/setup/AccountSetupBasics.java642
-rw-r--r--src/com/android/email/activity/setup/AccountSetupBasicsFragment.java35
-rw-r--r--src/com/android/email/activity/setup/AccountSetupCredentialsFragment.java248
-rw-r--r--src/com/android/email/activity/setup/AccountSetupFinal.java927
-rw-r--r--src/com/android/email/activity/setup/AccountSetupFragment.java40
-rw-r--r--src/com/android/email/activity/setup/AccountSetupIncoming.java203
-rw-r--r--src/com/android/email/activity/setup/AccountSetupIncomingFragment.java213
-rw-r--r--src/com/android/email/activity/setup/AccountSetupNamesFragment.java17
-rw-r--r--src/com/android/email/activity/setup/AccountSetupNoteDialogFragment.java76
-rw-r--r--src/com/android/email/activity/setup/AccountSetupOptionsFragment.java11
-rw-r--r--src/com/android/email/activity/setup/AccountSetupOutgoing.java118
-rw-r--r--src/com/android/email/activity/setup/AccountSetupOutgoingFragment.java180
-rw-r--r--src/com/android/email/activity/setup/AccountSetupType.java200
-rw-r--r--src/com/android/email/activity/setup/AccountSetupTypeFragment.java88
-rw-r--r--src/com/android/email/activity/setup/CheckSettingsErrorDialogFragment.java217
-rw-r--r--src/com/android/email/activity/setup/CheckSettingsProgressDialogFragment.java121
-rw-r--r--src/com/android/email/activity/setup/OAuthAuthenticationActivity.java2
-rw-r--r--src/com/android/email/activity/setup/SecurityRequiredDialogFragment.java98
-rw-r--r--src/com/android/email/activity/setup/SetupDataFragment.java182
-rw-r--r--src/com/android/email/activity/setup/SignInActivity.java169
-rw-r--r--src/com/android/email/activity/setup/SignInFragment.java197
-rw-r--r--src/com/android/email/mail/Sender.java9
-rw-r--r--src/com/android/email/mail/store/ImapStore.java9
-rw-r--r--src/com/android/email/mail/store/Pop3Store.java6
-rw-r--r--src/com/android/email/mail/transport/ExchangeSender.java64
-rw-r--r--src/com/android/email/mail/transport/SmtpSender.java6
-rw-r--r--src/com/android/email/service/AuthenticatorService.java4
-rw-r--r--src/com/android/email/service/EasTestAuthenticatorService.java4
35 files changed, 2464 insertions, 2873 deletions
diff --git a/src/com/android/email/activity/setup/AccountCheckSettingsFragment.java b/src/com/android/email/activity/setup/AccountCheckSettingsFragment.java
index 233a3b688..9918641e2 100644
--- a/src/com/android/email/activity/setup/AccountCheckSettingsFragment.java
+++ b/src/com/android/email/activity/setup/AccountCheckSettingsFragment.java
@@ -17,17 +17,11 @@
package com.android.email.activity.setup;
import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
-import android.app.ProgressDialog;
import android.content.Context;
-import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.text.TextUtils;
import com.android.email.R;
import com.android.email.mail.Sender;
@@ -69,28 +63,21 @@ public class AccountCheckSettingsFragment extends Fragment {
private final static int STATE_AUTODISCOVER_AUTH_DIALOG = 7; // terminal
private final static int STATE_AUTODISCOVER_RESULT = 8; // terminal
private int mState = STATE_START;
- private SetupDataFragment mSetupData;
+
+ // Args
+ private final static String ARGS_MODE = "mode";
+
+ private int mMode;
// Support for UI
private boolean mAttached;
private boolean mPaused = false;
- private CheckingDialog mCheckingDialog;
private MessagingException mProgressException;
// Support for AsyncTask and account checking
AccountCheckTask mAccountCheckTask;
- // Result codes returned by onCheckSettingsComplete.
- /** Check settings returned successfully */
- public final static int CHECK_SETTINGS_OK = 0;
- /** Check settings failed due to connection, authentication, or other server error */
- public final static int CHECK_SETTINGS_SERVER_ERROR = 1;
- /** Check settings failed due to user refusing to accept security requirements */
- public final static int CHECK_SETTINGS_SECURITY_USER_DENY = 2;
- /** Check settings failed due to certificate being required - user needs to pick immediately. */
- public final static int CHECK_SETTINGS_CLIENT_CERTIFICATE_NEEDED = 3;
-
- // Result codes returned by onAutoDiscoverComplete.
+ // Result codes returned by onCheckSettingsAutoDiscoverComplete.
/** AutoDiscover completed successfully with server setup data */
public final static int AUTODISCOVER_OK = 0;
/** AutoDiscover completed with no data (no server or AD not supported) */
@@ -101,18 +88,32 @@ public class AccountCheckSettingsFragment extends Fragment {
/**
* Callback interface for any target or activity doing account check settings
*/
- public interface Callbacks {
+ public interface Callback {
/**
* Called when CheckSettings completed
- * @param result check settings result code - success is CHECK_SETTINGS_OK
*/
- public void onCheckSettingsComplete(int result, SetupDataFragment setupData);
+ void onCheckSettingsComplete();
+
+ /**
+ * Called when we determine that a security policy will need to be installed
+ * @param hostName Passed back from the MessagingException
+ */
+ void onCheckSettingsSecurityRequired(String hostName);
+
+ /**
+ * Called when we receive an error while validating the account
+ * @param reason from
+ * {@link CheckSettingsErrorDialogFragment#getReasonFromException(MessagingException)}
+ * @param message from
+ * {@link CheckSettingsErrorDialogFragment#getErrorString(Context, MessagingException)}
+ */
+ void onCheckSettingsError(int reason, String message);
/**
* Called when autodiscovery completes.
* @param result autodiscovery result code - success is AUTODISCOVER_OK
*/
- public void onAutoDiscoverComplete(int result, SetupDataFragment setupData);
+ void onCheckSettingsAutoDiscoverComplete(int result);
}
// Public no-args constructor needed for fragment re-instantiation
@@ -123,9 +124,11 @@ public class AccountCheckSettingsFragment extends Fragment {
*
* @param mode incoming or outgoing
*/
- public static AccountCheckSettingsFragment newInstance(int mode, Fragment parentFragment) {
+ public static AccountCheckSettingsFragment newInstance(int mode) {
final AccountCheckSettingsFragment f = new AccountCheckSettingsFragment();
- f.setTargetFragment(parentFragment, mode);
+ final Bundle b = new Bundle(1);
+ b.putInt(ARGS_MODE, mode);
+ f.setArguments(b);
return f;
}
@@ -137,6 +140,7 @@ public class AccountCheckSettingsFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
+ mMode = getArguments().getInt(ARGS_MODE);
}
/**
@@ -152,13 +156,12 @@ public class AccountCheckSettingsFragment extends Fragment {
// If this is the first time, start the AsyncTask
if (mAccountCheckTask == null) {
- final int checkMode = getTargetRequestCode();
final SetupDataFragment.SetupDataContainer container =
(SetupDataFragment.SetupDataContainer) getActivity();
- mSetupData = container.getSetupData();
- final Account checkAccount = mSetupData.getAccount();
+ // TODO: don't pass in the whole SetupDataFragment
mAccountCheckTask = (AccountCheckTask)
- new AccountCheckTask(checkMode, checkAccount)
+ new AccountCheckTask(getActivity().getApplicationContext(), this, mMode,
+ container.getSetupData())
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
@@ -193,12 +196,6 @@ public class AccountCheckSettingsFragment extends Fragment {
Utility.cancelTaskInterrupt(mAccountCheckTask);
mAccountCheckTask = null;
}
- // Make doubly sure that the dialog isn't pointing at us before we're removed from the
- // fragment manager
- final Fragment f = getFragmentManager().findFragmentByTag(CheckingDialog.TAG);
- if (f != null) {
- f.setTargetFragment(null, 0);
- }
}
/**
@@ -228,64 +225,39 @@ public class AccountCheckSettingsFragment extends Fragment {
switch (newState) {
case STATE_CHECK_OK:
// immediately terminate, clean up, and report back
- // 1. get rid of progress dialog (if any)
- recoverAndDismissCheckingDialog();
- // 2. exit self
- fm.popBackStack();
- // 3. report OK back to target fragment or activity
- getCallbackTarget().onCheckSettingsComplete(CHECK_SETTINGS_OK, mSetupData);
+ getCallbackTarget().onCheckSettingsComplete();
break;
case STATE_CHECK_SHOW_SECURITY:
- // 1. get rid of progress dialog (if any)
- recoverAndDismissCheckingDialog();
- // 2. launch the error dialog, if needed
- if (fm.findFragmentByTag(SecurityRequiredDialog.TAG) == null) {
- String message = ex.getMessage();
- if (message != null) {
- message = message.trim();
- }
- SecurityRequiredDialog securityRequiredDialog =
- SecurityRequiredDialog.newInstance(this, message);
- fm.beginTransaction()
- .add(securityRequiredDialog, SecurityRequiredDialog.TAG)
- .commit();
+ // report that we need to accept a security policy
+ String hostName = ex.getMessage();
+ if (hostName != null) {
+ hostName = hostName.trim();
}
+ getCallbackTarget().onCheckSettingsSecurityRequired(hostName);
break;
case STATE_CHECK_ERROR:
case STATE_AUTODISCOVER_AUTH_DIALOG:
- // 1. get rid of progress dialog (if any)
- recoverAndDismissCheckingDialog();
- // 2. launch the error dialog, if needed
- if (fm.findFragmentByTag(ErrorDialog.TAG) == null) {
- ErrorDialog errorDialog = ErrorDialog.newInstance(
- getActivity(), this, mProgressException);
- fm.beginTransaction()
- .add(errorDialog, ErrorDialog.TAG)
- .commit();
- }
+ // report that we had an error
+ final int reason =
+ CheckSettingsErrorDialogFragment.getReasonFromException(ex);
+ final String errorMessage =
+ CheckSettingsErrorDialogFragment.getErrorString(getActivity(), ex);
+ getCallbackTarget().onCheckSettingsError(reason, errorMessage);
break;
case STATE_AUTODISCOVER_RESULT:
final HostAuth autoDiscoverResult = ((AutoDiscoverResults) ex).mHostAuth;
- // 1. get rid of progress dialog (if any)
- recoverAndDismissCheckingDialog();
- // 2. exit self
- fm.popBackStack();
- // 3. report back to target fragment or activity
- getCallbackTarget().onAutoDiscoverComplete(
- (autoDiscoverResult != null) ? AUTODISCOVER_OK : AUTODISCOVER_NO_DATA,
- mSetupData);
+ // report autodiscover results back to target fragment or activity
+ getCallbackTarget().onCheckSettingsAutoDiscoverComplete(
+ (autoDiscoverResult != null) ? AUTODISCOVER_OK : AUTODISCOVER_NO_DATA);
break;
default:
// Display a normal progress message
- mCheckingDialog = (CheckingDialog) fm.findFragmentByTag(CheckingDialog.TAG);
+ CheckSettingsProgressDialogFragment checkingDialog =
+ (CheckSettingsProgressDialogFragment)
+ fm.findFragmentByTag(CheckSettingsProgressDialogFragment.TAG);
- if (mCheckingDialog == null) {
- mCheckingDialog = CheckingDialog.newInstance(this, mState);
- fm.beginTransaction()
- .add(mCheckingDialog, CheckingDialog.TAG)
- .commit();
- } else {
- mCheckingDialog.updateProgress(mState);
+ if (checkingDialog != null) {
+ checkingDialog.updateProgress(mState);
}
break;
}
@@ -295,99 +267,19 @@ public class AccountCheckSettingsFragment extends Fragment {
/**
* Find the callback target, either a target fragment or the activity
*/
- private Callbacks getCallbackTarget() {
+ private Callback getCallbackTarget() {
final Fragment target = getTargetFragment();
- if (target instanceof Callbacks) {
- return (Callbacks) target;
+ if (target instanceof Callback) {
+ return (Callback) target;
}
Activity activity = getActivity();
- if (activity instanceof Callbacks) {
- return (Callbacks) activity;
+ if (activity instanceof Callback) {
+ return (Callback) activity;
}
throw new IllegalStateException();
}
/**
- * Recover and dismiss the progress dialog fragment
- */
- private void recoverAndDismissCheckingDialog() {
- if (mCheckingDialog == null) {
- mCheckingDialog = (CheckingDialog)
- getFragmentManager().findFragmentByTag(CheckingDialog.TAG);
- }
- if (mCheckingDialog != null) {
- // TODO: dismissAllowingStateLoss() can cause the fragment to return later as a zombie
- // after the fragment manager restores state, if it happens that this call is executed
- // after the state is saved. Figure out a way to clean this up later. b/11435698
- mCheckingDialog.dismissAllowingStateLoss();
- mCheckingDialog = null;
- }
- }
-
- /**
- * This is called when the user clicks "cancel" on the progress dialog. Shuts everything
- * down and dismisses everything.
- * This should cause us to remain in the current screen (not accepting the settings)
- */
- private void onCheckingDialogCancel() {
- // 1. kill the checker
- Utility.cancelTaskInterrupt(mAccountCheckTask);
- mAccountCheckTask = null;
- // 2. kill self with no report - this is "cancel"
- finish();
- }
-
- private void onEditCertificateOk() {
- getCallbackTarget().onCheckSettingsComplete(CHECK_SETTINGS_CLIENT_CERTIFICATE_NEEDED,
- mSetupData);
- finish();
- }
-
- /**
- * This is called when the user clicks "edit" from the error dialog. The error dialog
- * should have already dismissed itself.
- * Depending on the context, the target will remain in the current activity (e.g. editing
- * settings) or return to its own parent (e.g. enter new credentials).
- */
- private void onErrorDialogEditButton() {
- // 1. handle "edit" - notify callback that we had a problem with the test
- final Callbacks callbackTarget = getCallbackTarget();
- if (mState == STATE_AUTODISCOVER_AUTH_DIALOG) {
- // report auth error to target fragment or activity
- callbackTarget.onAutoDiscoverComplete(CHECK_SETTINGS_SERVER_ERROR, mSetupData);
- } else {
- // report check settings failure to target fragment or activity
- callbackTarget.onCheckSettingsComplete(CHECK_SETTINGS_SERVER_ERROR, mSetupData);
- }
- finish();
- }
-
- /** Kill self if not already killed. */
- private void finish() {
- final FragmentManager fm = getFragmentManager();
- if (fm != null) {
- fm.popBackStack();
- }
- }
-
- /**
- * This is called when the user clicks "ok" or "cancel" on the "security required" dialog.
- * Shuts everything down and dismisses everything, and reports the result appropriately.
- */
- private void onSecurityRequiredDialogResultOk(boolean okPressed) {
- // 1. handle OK/cancel - notify that security is OK and we can proceed
- final Callbacks callbackTarget = getCallbackTarget();
- callbackTarget.onCheckSettingsComplete(
- okPressed ? CHECK_SETTINGS_OK : CHECK_SETTINGS_SECURITY_USER_DENY, mSetupData);
-
- // 2. kill self if not already killed by callback
- final FragmentManager fm = getFragmentManager();
- if (fm != null) {
- fm.popBackStack();
- }
- }
-
- /**
* This exception class is used to report autodiscover results via the reporting mechanism.
*/
public static class AutoDiscoverResults extends MessagingException {
@@ -414,10 +306,11 @@ public class AccountCheckSettingsFragment extends Fragment {
* TODO: It would be better to remove the UI complete from here (the exception->string
* conversions).
*/
- private class AccountCheckTask extends AsyncTask<Void, Integer, MessagingException> {
-
+ private static class AccountCheckTask extends AsyncTask<Void, Integer, MessagingException> {
final Context mContext;
+ final AccountCheckSettingsFragment mCallback;
final int mMode;
+ final SetupDataFragment mSetupData;
final Account mAccount;
final String mStoreHost;
final String mCheckEmail;
@@ -425,16 +318,20 @@ public class AccountCheckSettingsFragment extends Fragment {
/**
* Create task and parameterize it
+ * @param context application context object
* @param mode bits request operations
- * @param checkAccount account holding values to be checked
+ * @param setupData {@link SetupDataFragment} holding values to be checked
*/
- public AccountCheckTask(int mode, Account checkAccount) {
- mContext = getActivity().getApplicationContext();
+ public AccountCheckTask(Context context, AccountCheckSettingsFragment callback, int mode,
+ SetupDataFragment setupData) {
+ mContext = context;
+ mCallback = callback;
mMode = mode;
- mAccount = checkAccount;
- mStoreHost = checkAccount.mHostAuthRecv.mAddress;
- mCheckEmail = checkAccount.mEmailAddress;
- mCheckPassword = checkAccount.mHostAuthRecv.mPassword;
+ mSetupData = setupData;
+ mAccount = setupData.getAccount();
+ mStoreHost = mAccount.mHostAuthRecv.mAddress;
+ mCheckEmail = mAccount.mEmailAddress;
+ mCheckPassword = mAccount.mHostAuthRecv.mPassword;
}
@Override
@@ -539,7 +436,7 @@ public class AccountCheckSettingsFragment extends Fragment {
@Override
protected void onProgressUpdate(Integer... progress) {
if (isCancelled()) return;
- reportProgress(progress[0], null);
+ mCallback.reportProgress(progress[0], null);
}
/**
@@ -555,7 +452,7 @@ public class AccountCheckSettingsFragment extends Fragment {
protected void onPostExecute(MessagingException result) {
if (isCancelled()) return;
if (result == null) {
- reportProgress(STATE_CHECK_OK, null);
+ mCallback.reportProgress(STATE_CHECK_OK, null);
} else {
int progressState = STATE_CHECK_ERROR;
final int exceptionType = result.getExceptionType();
@@ -575,358 +472,47 @@ public class AccountCheckSettingsFragment extends Fragment {
progressState = STATE_CHECK_SHOW_SECURITY;
break;
}
- reportProgress(progressState, result);
+ mCallback.reportProgress(progressState, result);
}
}
}
- private static String getErrorString(Context context, MessagingException ex) {
- final int id;
- String message = ex.getMessage();
- if (message != null) {
- message = message.trim();
- }
- switch (ex.getExceptionType()) {
- // The remaining exception types are handled by setting the state to
- // STATE_CHECK_ERROR (above, default) and conversion to specific error strings.
- case MessagingException.CERTIFICATE_VALIDATION_ERROR:
- id = TextUtils.isEmpty(message)
- ? R.string.account_setup_failed_dlg_certificate_message
- : R.string.account_setup_failed_dlg_certificate_message_fmt;
- break;
- case MessagingException.AUTHENTICATION_FAILED:
- id = R.string.account_setup_failed_dlg_auth_message;
- break;
- case MessagingException.AUTODISCOVER_AUTHENTICATION_FAILED:
- id = R.string.account_setup_autodiscover_dlg_authfail_message;
- break;
- case MessagingException.AUTHENTICATION_FAILED_OR_SERVER_ERROR:
- id = R.string.account_setup_failed_check_credentials_message;
- break;
- case MessagingException.IOERROR:
- id = R.string.account_setup_failed_ioerror;
- break;
- case MessagingException.TLS_REQUIRED:
- id = R.string.account_setup_failed_tls_required;
- break;
- case MessagingException.AUTH_REQUIRED:
- id = R.string.account_setup_failed_auth_required;
- break;
- case MessagingException.SECURITY_POLICIES_UNSUPPORTED:
- id = R.string.account_setup_failed_security_policies_unsupported;
- // Belt and suspenders here; there should always be a non-empty array here
- String[] unsupportedPolicies = (String[]) ex.getExceptionData();
- if (unsupportedPolicies == null) {
- LogUtils.w(Logging.LOG_TAG, "No data for unsupported policies");
- break;
- }
- // Build a string, concatenating policies we don't support
- final StringBuilder sb = new StringBuilder();
- boolean first = true;
- for (String policyName: unsupportedPolicies) {
- if (first) {
- first = false;
- } else {
- sb.append(", ");
- }
- sb.append(policyName);
- }
- message = sb.toString();
- break;
- case MessagingException.ACCESS_DENIED:
- id = R.string.account_setup_failed_access_denied;
- break;
- case MessagingException.PROTOCOL_VERSION_UNSUPPORTED:
- id = R.string.account_setup_failed_protocol_unsupported;
- break;
- case MessagingException.GENERAL_SECURITY:
- id = R.string.account_setup_failed_security;
- break;
- case MessagingException.CLIENT_CERTIFICATE_REQUIRED:
- id = R.string.account_setup_failed_certificate_required;
+ /**
+ * Convert progress to message
+ */
+ protected static String getProgressString(Context context, int progress) {
+ int stringId = 0;
+ switch (progress) {
+ case STATE_CHECK_AUTODISCOVER:
+ stringId = R.string.account_setup_check_settings_retr_info_msg;
break;
- case MessagingException.CLIENT_CERTIFICATE_ERROR:
- id = R.string.account_setup_failed_certificate_inaccessible;
+ case STATE_START:
+ case STATE_CHECK_INCOMING:
+ stringId = R.string.account_setup_check_settings_check_incoming_msg;
break;
- default:
- id = TextUtils.isEmpty(message)
- ? R.string.account_setup_failed_dlg_server_message
- : R.string.account_setup_failed_dlg_server_message_fmt;
+ case STATE_CHECK_OUTGOING:
+ stringId = R.string.account_setup_check_settings_check_outgoing_msg;
break;
}
- return TextUtils.isEmpty(message)
- ? context.getString(id)
- : context.getString(id, message);
- }
-
- /**
- * Simple dialog that shows progress as we work through the settings checks.
- * This is stateless except for its UI (e.g. current strings) and can be torn down or
- * recreated at any time without affecting the account checking progress.
- */
- public static class CheckingDialog extends DialogFragment {
- @SuppressWarnings("hiding")
- public final static String TAG = "CheckProgressDialog";
-
- // Extras for saved instance state
- private final String EXTRA_PROGRESS_STRING = "CheckProgressDialog.Progress";
-
- // UI
- private String mProgressString;
-
- // Public no-args constructor needed for fragment re-instantiation
- public CheckingDialog() {}
-
- /**
- * Create a dialog that reports progress
- * @param progress initial progress indication
- */
- public static CheckingDialog newInstance(AccountCheckSettingsFragment parentFragment,
- int progress) {
- final CheckingDialog f = new CheckingDialog();
- f.setTargetFragment(parentFragment, progress);
- return f;
- }
-
- /**
- * Update the progress of an existing dialog
- * @param progress latest progress to be displayed
- */
- public void updateProgress(int progress) {
- mProgressString = getProgressString(progress);
- final AlertDialog dialog = (AlertDialog) getDialog();
- if (dialog != null && mProgressString != null) {
- dialog.setMessage(mProgressString);
- }
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Context context = getActivity();
- if (savedInstanceState != null) {
- mProgressString = savedInstanceState.getString(EXTRA_PROGRESS_STRING);
- }
- if (mProgressString == null) {
- mProgressString = getProgressString(getTargetRequestCode());
- }
-
- final ProgressDialog dialog = new ProgressDialog(context);
- dialog.setIndeterminate(true);
- dialog.setMessage(mProgressString);
- dialog.setButton(DialogInterface.BUTTON_NEGATIVE,
- context.getString(R.string.cancel_action),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismiss();
-
- final AccountCheckSettingsFragment target =
- (AccountCheckSettingsFragment) getTargetFragment();
- if (target != null) {
- target.onCheckingDialogCancel();
- }
- }
- });
- return dialog;
- }
-
- /**
- * Listen for cancellation, which can happen from places other than the
- * negative button (e.g. touching outside the dialog), and stop the checker
- */
- @Override
- public void onCancel(DialogInterface dialog) {
- final AccountCheckSettingsFragment target =
- (AccountCheckSettingsFragment) getTargetFragment();
- if (target != null) {
- target.onCheckingDialogCancel();
- }
- super.onCancel(dialog);
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putString(EXTRA_PROGRESS_STRING, mProgressString);
- }
-
- /**
- * Convert progress to message
- */
- private String getProgressString(int progress) {
- int stringId = 0;
- switch (progress) {
- case STATE_CHECK_AUTODISCOVER:
- stringId = R.string.account_setup_check_settings_retr_info_msg;
- break;
- case STATE_CHECK_INCOMING:
- stringId = R.string.account_setup_check_settings_check_incoming_msg;
- break;
- case STATE_CHECK_OUTGOING:
- stringId = R.string.account_setup_check_settings_check_outgoing_msg;
- break;
- }
- return getActivity().getString(stringId);
- }
- }
-
- /**
- * The standard error dialog. Calls back to onErrorDialogButton().
- */
- public static class ErrorDialog extends DialogFragment {
- @SuppressWarnings("hiding")
- public final static String TAG = "ErrorDialog";
-
- // Bundle keys for arguments
- private final static String ARGS_MESSAGE = "ErrorDialog.Message";
- private final static String ARGS_EXCEPTION_ID = "ErrorDialog.ExceptionId";
-
- /**
- * Use {@link #newInstance} This public constructor is still required so
- * that DialogFragment state can be automatically restored by the
- * framework.
- */
- public ErrorDialog() {
- }
-
- public static ErrorDialog newInstance(Context context, AccountCheckSettingsFragment target,
- MessagingException ex) {
- final ErrorDialog fragment = new ErrorDialog();
- final Bundle arguments = new Bundle(2);
- arguments.putString(ARGS_MESSAGE, getErrorString(context, ex));
- arguments.putInt(ARGS_EXCEPTION_ID, ex.getExceptionType());
- fragment.setArguments(arguments);
- fragment.setTargetFragment(target, 0);
- return fragment;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Context context = getActivity();
- final Bundle arguments = getArguments();
- final String message = arguments.getString(ARGS_MESSAGE);
- final int exceptionId = arguments.getInt(ARGS_EXCEPTION_ID);
- final AccountCheckSettingsFragment target =
- (AccountCheckSettingsFragment) getTargetFragment();
-
- final AlertDialog.Builder builder = new AlertDialog.Builder(context)
- .setMessage(message)
- .setCancelable(true);
-
- // Use a different title when we get
- // MessagingException.AUTODISCOVER_AUTHENTICATION_FAILED
- if (exceptionId == MessagingException.AUTODISCOVER_AUTHENTICATION_FAILED) {
- builder.setTitle(R.string.account_setup_autodiscover_dlg_authfail_title);
- } else {
- builder.setIconAttribute(android.R.attr.alertDialogIcon)
- .setTitle(context.getString(R.string.account_setup_failed_dlg_title));
- }
-
- if (exceptionId == MessagingException.CLIENT_CERTIFICATE_REQUIRED) {
- // Certificate error - show two buttons so the host fragment can auto pop
- // into the appropriate flow.
- builder.setPositiveButton(
- context.getString(android.R.string.ok),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismiss();
- target.onEditCertificateOk();
- }
- });
- builder.setNegativeButton(
- context.getString(android.R.string.cancel),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismiss();
- target.onErrorDialogEditButton();
- }
- });
-
- } else {
- // "Normal" error - just use a single "Edit details" button.
- builder.setPositiveButton(
- context.getString(R.string.account_setup_failed_dlg_edit_details_action),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismiss();
- target.onErrorDialogEditButton();
- }
- });
- }
-
- return builder.create();
+ if (stringId != 0) {
+ return context.getString(stringId);
+ } else {
+ return null;
}
-
}
/**
- * The "security required" error dialog. This is presented whenever an exchange account
- * reports that it will require security policy control, and provide the user with the
- * opportunity to accept or deny this.
- *
- * If the user clicks OK, calls onSecurityRequiredDialogResultOk(true) which reports back
- * to the target as if the settings check was "ok". If the user clicks "cancel", calls
- * onSecurityRequiredDialogResultOk(false) which simply closes the checker (this is the
- * same as any other failed check.)
+ * Convert mode to initial progress
*/
- public static class SecurityRequiredDialog extends DialogFragment {
- @SuppressWarnings("hiding")
- public final static String TAG = "SecurityRequiredDialog";
-
- // Bundle keys for arguments
- private final static String ARGS_HOST_NAME = "SecurityRequiredDialog.HostName";
-
- // Public no-args constructor needed for fragment re-instantiation
- public SecurityRequiredDialog() {}
-
- public static SecurityRequiredDialog newInstance(AccountCheckSettingsFragment target,
- String hostName) {
- final SecurityRequiredDialog fragment = new SecurityRequiredDialog();
- final Bundle arguments = new Bundle(1);
- arguments.putString(ARGS_HOST_NAME, hostName);
- fragment.setArguments(arguments);
- fragment.setTargetFragment(target, 0);
- return fragment;
+ protected static int getProgressForMode(int checkMode) {
+ switch (checkMode) {
+ case SetupDataFragment.CHECK_INCOMING:
+ return STATE_CHECK_INCOMING;
+ case SetupDataFragment.CHECK_OUTGOING:
+ return STATE_CHECK_OUTGOING;
+ case SetupDataFragment.CHECK_AUTODISCOVER:
+ return STATE_CHECK_AUTODISCOVER;
}
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Context context = getActivity();
- final Bundle arguments = getArguments();
- final String hostName = arguments.getString(ARGS_HOST_NAME);
- final AccountCheckSettingsFragment target =
- (AccountCheckSettingsFragment) getTargetFragment();
-
- return new AlertDialog.Builder(context)
- .setIconAttribute(android.R.attr.alertDialogIcon)
- .setTitle(context.getString(R.string.account_setup_security_required_title))
- .setMessage(context.getString(
- R.string.account_setup_security_policies_required_fmt, hostName))
- .setCancelable(true)
- .setPositiveButton(
- context.getString(R.string.okay_action),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismiss();
- target.onSecurityRequiredDialogResultOk(true);
- }
- })
- .setNegativeButton(
- context.getString(R.string.cancel_action),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismiss();
- target.onSecurityRequiredDialogResultOk(false);
- }
- })
- .create();
- }
-
+ return STATE_START;
}
-
}
diff --git a/src/com/android/email/activity/setup/AccountCreationFragment.java b/src/com/android/email/activity/setup/AccountCreationFragment.java
index bca76cf7b..e5736774c 100644
--- a/src/com/android/email/activity/setup/AccountCreationFragment.java
+++ b/src/com/android/email/activity/setup/AccountCreationFragment.java
@@ -44,6 +44,8 @@ import java.io.IOException;
* related background tasks.
*/
public class AccountCreationFragment extends Fragment {
+ public static final String TAG = "AccountCreationFragment";
+
public static final int REQUEST_CODE_ACCEPT_POLICIES = 1;
private static final String ACCOUNT_TAG = "account";
@@ -62,6 +64,13 @@ public class AccountCreationFragment extends Fragment {
private Context mAppContext;
private final Handler mHandler;
+ public interface Callback {
+ void onAccountCreationFragmentComplete();
+ void destroyAccountCreationFragment();
+ void showCreateAccountErrorDialog();
+ void setAccount(Account account);
+ }
+
public AccountCreationFragment() {
mHandler = new Handler();
}
@@ -84,13 +93,18 @@ public class AccountCreationFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
- mAppContext = getActivity().getApplicationContext();
if (savedInstanceState != null) {
mStage = savedInstanceState.getInt(SAVESTATE_STAGE);
}
}
@Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ mAppContext = getActivity().getApplicationContext();
+ }
+
+ @Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(SAVESTATE_STAGE, mStage);
@@ -210,7 +224,7 @@ public class AccountCreationFragment extends Fragment {
mStage = STAGE_REFRESHING_ACCOUNT;
kickRefreshingAccountLoader();
} else {
- final AccountSetupFinal activity = (AccountSetupFinal)getActivity();
+ final Callback callback = (Callback) getActivity();
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -218,8 +232,8 @@ public class AccountCreationFragment extends Fragment {
return;
}
// Can't do this from within onLoadFinished
- activity.destroyAccountCreationFragment();
- activity.showCreateAccountErrorDialog();
+ callback.destroyAccountCreationFragment();
+ callback.showCreateAccountErrorDialog();
}
});
}
@@ -303,13 +317,13 @@ public class AccountCreationFragment extends Fragment {
}
// Move to final setup screen
- AccountSetupFinal activity = (AccountSetupFinal) getActivity();
- activity.getSetupData().setAccount(account);
- activity.proceedFromAccountCreationFragment();
+ Callback callback = (Callback) getActivity();
+ callback.setAccount(account);
+ callback.onAccountCreationFragmentComplete();
// Update the folder list (to get our starting folders, e.g. Inbox)
- final EmailServiceProxy proxy = EmailServiceUtils.getServiceForAccount(activity,
- account.mId);
+ final EmailServiceProxy proxy = EmailServiceUtils
+ .getServiceForAccount(mAppContext, account.mId);
try {
proxy.updateFolderList(account.mId);
} catch (RemoteException e) {
diff --git a/src/com/android/email/activity/setup/AccountCredentials.java b/src/com/android/email/activity/setup/AccountCredentials.java
new file mode 100644
index 000000000..9b2b43cd3
--- /dev/null
+++ b/src/com/android/email/activity/setup/AccountCredentials.java
@@ -0,0 +1,77 @@
+package com.android.email.activity.setup;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+import com.android.email.R;
+import com.android.email.activity.UiUtilities;
+
+public class AccountCredentials extends Activity
+ implements AccountSetupCredentialsFragment.Callback, View.OnClickListener {
+
+ private View mDoneButton;
+ private View mCancelButton;
+
+ private static final String EXTRA_EMAIL = "email";
+ private static final String EXTRA_PROTOCOL = "protocol";
+
+ private static final String CREDENTIALS_FRAGMENT_TAG = "credentials";
+
+ public static Intent getAccountCredentialsIntent(final Context context, final String email,
+ final String protocol) {
+ final Intent i = new Intent(context, AccountCredentials.class);
+ i.putExtra(EXTRA_EMAIL, email);
+ i.putExtra(EXTRA_PROTOCOL, protocol);
+ return i;
+ }
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.account_credentials);
+ final String emailAddress = getIntent().getStringExtra(EXTRA_EMAIL);
+ final String protocol = getIntent().getStringExtra(EXTRA_PROTOCOL);
+
+ final AccountSetupCredentialsFragment f =
+ AccountSetupCredentialsFragment.newInstance(emailAddress, protocol);
+ getFragmentManager().beginTransaction()
+ .add(R.id.account_credentials_fragment_container, f, CREDENTIALS_FRAGMENT_TAG)
+ .commit();
+
+ mDoneButton = UiUtilities.getView(this, R.id.done);
+ mCancelButton = UiUtilities.getView(this, R.id.cancel);
+ mDoneButton.setOnClickListener(this);
+ mCancelButton.setOnClickListener(this);
+
+ // Assume canceled until we find out otherwise.
+ setResult(RESULT_CANCELED);
+ }
+
+ @Override
+ public void onCredentialsComplete(Bundle results) {
+ final Intent intent = new Intent();
+ intent.putExtras(results);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view == mDoneButton) {
+ final AccountSetupCredentialsFragment fragment = (AccountSetupCredentialsFragment)
+ getFragmentManager().findFragmentByTag(CREDENTIALS_FRAGMENT_TAG);
+ final Bundle results = fragment.getCredentialResults();
+ onCredentialsComplete(results);
+ } else if (view == mCancelButton) {
+ finish();
+ }
+ }
+
+ @Override
+ public void setNextButtonEnabled(boolean enabled) {
+ mDoneButton.setEnabled(enabled);
+ }
+}
diff --git a/src/com/android/email/activity/setup/AccountFinalizeFragment.java b/src/com/android/email/activity/setup/AccountFinalizeFragment.java
new file mode 100644
index 000000000..4880d0de2
--- /dev/null
+++ b/src/com/android/email/activity/setup/AccountFinalizeFragment.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.email.activity.setup;
+
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Loader;
+import android.os.Bundle;
+import android.os.Handler;
+
+import com.android.email.provider.AccountBackupRestore;
+import com.android.emailcommon.provider.Account;
+import com.android.emailcommon.provider.EmailContent;
+import com.android.mail.ui.MailAsyncTaskLoader;
+
+public class AccountFinalizeFragment extends Fragment {
+ public static final String TAG = "AccountFinalizeFragment";
+
+ private static final String ACCOUNT_TAG = "account";
+
+ private static final int FINAL_ACCOUNT_TASK_LOADER_ID = 0;
+
+ private Context mAppContext;
+ private final Handler mHandler = new Handler();
+
+ public interface Callback {
+ void onAccountFinalizeFragmentComplete();
+ }
+
+ public AccountFinalizeFragment() {}
+
+ public static AccountFinalizeFragment newInstance(Account account) {
+ final AccountFinalizeFragment f = new AccountFinalizeFragment();
+ final Bundle args = new Bundle(1);
+ args.putParcelable(ACCOUNT_TAG, account);
+ f.setArguments(args);
+ return f;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mAppContext = getActivity().getApplicationContext();
+
+ setRetainInstance(true);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ getLoaderManager().initLoader(FINAL_ACCOUNT_TASK_LOADER_ID, getArguments(),
+ new LoaderManager.LoaderCallbacks<Boolean>() {
+ @Override
+ public Loader<Boolean> onCreateLoader(int id, Bundle args) {
+ final Account accountArg = args.getParcelable(ACCOUNT_TAG);
+ return new FinalSetupTaskLoader(mAppContext, accountArg);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Boolean> loader, Boolean success) {
+ if (!success) {
+ return;
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (isResumed()) {
+ Callback activity = (Callback) getActivity();
+ activity.onAccountFinalizeFragmentComplete();
+ }
+ }
+ });
+
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Boolean> loader) {}
+ });
+ }
+
+ /**
+ * Final account setup work is handled in this Loader:
+ * Commit final values to provider
+ * Trigger account backup
+ */
+ private static class FinalSetupTaskLoader extends MailAsyncTaskLoader<Boolean> {
+
+ private final Account mAccount;
+
+ public FinalSetupTaskLoader(Context context, Account account) {
+ super(context);
+ mAccount = account;
+ }
+
+ Account getAccount() {
+ return mAccount;
+ }
+
+ @Override
+ public Boolean loadInBackground() {
+ // Update the account in the database
+ final ContentValues cv = new ContentValues();
+ cv.put(EmailContent.AccountColumns.DISPLAY_NAME, mAccount.getDisplayName());
+ cv.put(EmailContent.AccountColumns.SENDER_NAME, mAccount.getSenderName());
+ mAccount.update(getContext(), cv);
+
+ // Update the backup (side copy) of the accounts
+ AccountBackupRestore.backup(getContext());
+
+ return true;
+ }
+
+ @Override
+ protected void onDiscardResult(Boolean result) {}
+ }
+}
diff --git a/src/com/android/email/activity/setup/AccountServerBaseFragment.java b/src/com/android/email/activity/setup/AccountServerBaseFragment.java
index 3c136b192..c9b8e20ee 100644
--- a/src/com/android/email/activity/setup/AccountServerBaseFragment.java
+++ b/src/com/android/email/activity/setup/AccountServerBaseFragment.java
@@ -17,18 +17,17 @@
package com.android.email.activity.setup;
import android.app.Activity;
-import android.app.Fragment;
+import android.app.LoaderManager;
import android.content.Context;
-import android.os.AsyncTask;
+import android.content.Loader;
import android.os.Bundle;
+import android.os.Handler;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
-import android.widget.Button;
-import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
@@ -45,64 +44,49 @@ import com.android.emailcommon.provider.HostAuth;
* Activity callback during onAttach
* Present "Next" button and respond to its clicks
*/
-public abstract class AccountServerBaseFragment extends Fragment
- implements AccountCheckSettingsFragment.Callbacks, OnClickListener {
+public abstract class AccountServerBaseFragment extends AccountSetupFragment
+ implements OnClickListener {
private static final String BUNDLE_KEY_SETTINGS = "AccountServerBaseFragment.settings";
private static final String BUNDLE_KEY_ACTIVITY_TITLE = "AccountServerBaseFragment.title";
+ private static final String BUNDLE_KEY_SAVING = "AccountServerBaseFragment.saving";
- protected Activity mContext;
- protected Callback mCallback = EmptyCallback.INSTANCE;
+ protected Context mAppContext;
/**
* Whether or not we are in "settings mode". We re-use the same screens for both the initial
- * account creation as well as subsequent account modification. If <code>mSettingsMode</code>
- * if <code>false</code>, we are in account creation mode. Otherwise, we are in account
+ * account creation as well as subsequent account modification. If this is
+ * <code>false</code>, we are in account creation mode. Otherwise, we are in account
* modification mode.
*/
protected boolean mSettingsMode;
- /*package*/ HostAuth mLoadedSendAuth;
- /*package*/ HostAuth mLoadedRecvAuth;
+ protected HostAuth mLoadedSendAuth;
+ protected HostAuth mLoadedRecvAuth;
protected SetupDataFragment mSetupData;
// This is null in the setup wizard screens, and non-null in AccountSettings mode
private View mProceedButton;
+ protected String mBaseScheme = "protocol";
- // This is used to debounce multiple clicks on the proceed button (which does async work)
- private boolean mProceedButtonPressed;
- /*package*/ String mBaseScheme = "protocol";
+ // Set to true if we're in the process of saving
+ private boolean mSaving;
/**
- * Callback interface that owning activities must provide
+ // Used to post the callback once we're done saving, since we can't perform fragment
+ // transactions from {@link LoaderManager.LoaderCallbacks#onLoadFinished(Loader, Object)}
*/
- public interface Callback {
- /**
- * Called each time the user-entered input transitions between valid and invalid
- * @param enable true to enable proceed/next button, false to disable
- */
- public void onEnableProceedButtons(boolean enable);
+ private Handler mHandler = new Handler();
+ /**
+ * Callback interface that owning activities must provide
+ */
+ public interface Callback extends AccountSetupFragment.Callback {
/**
* Called when user clicks "next". Starts account checker.
* @param checkMode values from {@link SetupDataFragment}
- * @param target the fragment that requested the check
- */
- public void onProceedNext(int checkMode, AccountServerBaseFragment target);
-
- /**
- * Called when account checker completes. Fragments are responsible for saving
- * own edited data; This is primarily for the activity to do post-check navigation.
- * @param result check settings result code - success is CHECK_SETTINGS_OK
- * @param setupData possibly modified SetupData
*/
- public void onCheckSettingsComplete(int result, SetupDataFragment setupData);
- }
-
- private static class EmptyCallback implements Callback {
- public static final Callback INSTANCE = new EmptyCallback();
- @Override public void onEnableProceedButtons(boolean enable) { }
- @Override public void onProceedNext(int checkMode, AccountServerBaseFragment target) { }
- @Override public void onCheckSettingsComplete(int result, SetupDataFragment setupData) { }
+ public void onAccountServerUIComplete(int checkMode);
+ public void onAccountServerSaveComplete();
}
/**
@@ -111,7 +95,7 @@ public abstract class AccountServerBaseFragment extends Fragment
* @param settingsMode True if we're in settings, false if we're in account creation
* @return Arg bundle
*/
- public static Bundle getArgs(Boolean settingsMode) {
+ public static Bundle getArgs(boolean settingsMode) {
final Bundle setupModeArgs = new Bundle(1);
setupModeArgs.putBoolean(BUNDLE_KEY_SETTINGS, settingsMode);
return setupModeArgs;
@@ -130,6 +114,7 @@ public abstract class AccountServerBaseFragment extends Fragment
mSettingsMode = false;
if (savedInstanceState != null) {
mSettingsMode = savedInstanceState.getBoolean(BUNDLE_KEY_SETTINGS);
+ mSaving = savedInstanceState.getBoolean(BUNDLE_KEY_SAVING);
} else if (getArguments() != null) {
mSettingsMode = getArguments().getBoolean(BUNDLE_KEY_SETTINGS);
}
@@ -150,38 +135,40 @@ public abstract class AccountServerBaseFragment extends Fragment
@Override
public void onActivityCreated(Bundle savedInstanceState) {
- // startPreferencePanel launches this fragment with the right title initially, but
- // if the device is rotate we must set the title ourselves
- mContext = getActivity();
+ final Activity activity = getActivity();
+ mAppContext = activity.getApplicationContext();
if (mSettingsMode && savedInstanceState != null) {
- mContext.setTitle(savedInstanceState.getString(BUNDLE_KEY_ACTIVITY_TITLE));
+ // startPreferencePanel launches this fragment with the right title initially, but
+ // if the device is rotated we must set the title ourselves
+ activity.setTitle(savedInstanceState.getString(BUNDLE_KEY_ACTIVITY_TITLE));
}
SetupDataFragment.SetupDataContainer container =
- (SetupDataFragment.SetupDataContainer) mContext;
+ (SetupDataFragment.SetupDataContainer) activity;
mSetupData = container.getSetupData();
super.onActivityCreated(savedInstanceState);
}
@Override
- public void onSaveInstanceState(Bundle outState) {
- outState.putString(BUNDLE_KEY_ACTIVITY_TITLE, (String) getActivity().getTitle());
- outState.putBoolean(BUNDLE_KEY_SETTINGS, mSettingsMode);
+ public void onResume() {
+ super.onResume();
+ if (mSaving) {
+ // We need to call this here in case the save completed while we weren't resumed
+ saveSettings();
+ }
}
@Override
- public void onDetach() {
- super.onDetach();
-
- // Ensure that we don't have any callbacks at this point.
- mCallback = EmptyCallback.INSTANCE;
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putString(BUNDLE_KEY_ACTIVITY_TITLE, (String) getActivity().getTitle());
+ outState.putBoolean(BUNDLE_KEY_SETTINGS, mSettingsMode);
}
@Override
public void onPause() {
// Hide the soft keyboard if we lose focus
final InputMethodManager imm =
- (InputMethodManager)mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+ (InputMethodManager) mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getView().getWindowToken(), 0);
super.onPause();
}
@@ -196,25 +183,12 @@ public abstract class AccountServerBaseFragment extends Fragment
getActivity().onBackPressed();
break;
case R.id.done:
- // Simple debounce - just ignore while checks are underway
- if (mProceedButtonPressed) {
- return;
- }
- mProceedButtonPressed = true;
- onNext();
+ collectUserInput();
break;
}
}
/**
- * Activity provides callbacks here.
- */
- public void setCallback(Callback callback) {
- mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback;
- mContext = getActivity();
- }
-
- /**
* Enable/disable the "next" button
*/
public void enableNextButton(boolean enable) {
@@ -223,10 +197,11 @@ public abstract class AccountServerBaseFragment extends Fragment
if (mProceedButton != null) {
mProceedButton.setEnabled(enable);
}
- clearButtonBounce();
- // TODO: This supports the phone UX activities and will be removed
- mCallback.onEnableProceedButtons(enable);
+ final Callback callback = (Callback) getActivity();
+ if (callback != null) {
+ callback.setNextButtonEnabled(enable);
+ }
}
/**
@@ -243,7 +218,7 @@ public abstract class AccountServerBaseFragment extends Fragment
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
// Framework will not auto-hide IME; do it ourselves
- InputMethodManager imm = (InputMethodManager)mContext.
+ InputMethodManager imm = (InputMethodManager) mAppContext.
getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getView().getWindowToken(), 0);
view.setError(errorMessage);
@@ -292,80 +267,49 @@ public abstract class AccountServerBaseFragment extends Fragment
};
/**
- * Clears the "next" button de-bounce flags and allows the "next" button to activate.
- */
- protected void clearButtonBounce() {
- mProceedButtonPressed = false;
- }
-
- /**
- * Implements AccountCheckSettingsFragment.Callbacks
- *
- * Handle OK or error result from check settings. Save settings (async), and then
- * exit to previous fragment.
- */
- @Override
- public void onCheckSettingsComplete(final int settingsResult, SetupDataFragment setupData) {
- mSetupData = setupData;
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- if (settingsResult == AccountCheckSettingsFragment.CHECK_SETTINGS_OK) {
- if (mSetupData.getFlowMode() == SetupDataFragment.FLOW_MODE_EDIT) {
- saveSettingsAfterEdit();
- } else {
- saveSettingsAfterSetup();
- }
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- // Signal to owning activity that a settings check completed
- mCallback.onCheckSettingsComplete(settingsResult, mSetupData);
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
- /**
- * Implements AccountCheckSettingsFragment.Callbacks
- * This is overridden only by AccountSetupExchange
- */
- @Override
- public void onAutoDiscoverComplete(int result, SetupDataFragment setupData) {
- throw new IllegalStateException();
- }
-
- /**
* Returns whether or not any settings have changed.
*/
public boolean haveSettingsChanged() {
final Account account = mSetupData.getAccount();
- final HostAuth sendAuth = account.getOrCreateHostAuthSend(mContext);
+ final HostAuth sendAuth = account.getOrCreateHostAuthSend(mAppContext);
final boolean sendChanged = (mLoadedSendAuth != null && !mLoadedSendAuth.equals(sendAuth));
- final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
+ final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mAppContext);
final boolean recvChanged = (mLoadedRecvAuth != null && !mLoadedRecvAuth.equals(recvAuth));
return sendChanged || recvChanged;
}
- /**
- * Save settings after "OK" result from checker. Concrete classes must implement.
- * This is called from a worker thread and is allowed to perform DB operations.
- */
- public abstract void saveSettingsAfterEdit();
+ public void saveSettings() {
+ getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Boolean>() {
+ @Override
+ public Loader<Boolean> onCreateLoader(int id, Bundle args) {
+ return getSaveSettingsLoader();
+ }
- /**
- * Save settings after "OK" result from checker. Concrete classes must implement.
- * This is called from a worker thread and is allowed to perform DB operations.
- */
- public abstract void saveSettingsAfterSetup();
+ @Override
+ public void onLoadFinished(Loader<Boolean> loader, Boolean data) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (isResumed()) {
+ final Callback callback = (Callback) getActivity();
+ callback.onAccountServerSaveComplete();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Boolean> loader) {}
+ });
+ }
+
+ public abstract Loader<Boolean> getSaveSettingsLoader();
/**
- * Respond to a click of the "Next" button. Concrete classes must implement.
+ * Collect the user's input into the setup data object. Concrete classes must implement.
*/
- public abstract void onNext();
+ public abstract void collectUserInput();
}
diff --git a/src/com/android/email/activity/setup/AccountSettings.java b/src/com/android/email/activity/setup/AccountSettings.java
index 9cce53cda..2e9ef22d2 100644
--- a/src/com/android/email/activity/setup/AccountSettings.java
+++ b/src/com/android/email/activity/setup/AccountSettings.java
@@ -17,7 +17,6 @@
package com.android.email.activity.setup;
import android.app.ActionBar;
-import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@@ -72,7 +71,9 @@ import java.util.List;
* dealing with accounts being added/deleted and triggering the header reload.
*/
public class AccountSettings extends PreferenceActivity implements FeedbackEnabledActivity,
- SetupDataFragment.SetupDataContainer {
+ SetupDataFragment.SetupDataContainer, SecurityRequiredDialogFragment.Callback,
+ CheckSettingsErrorDialogFragment.Callback, AccountCheckSettingsFragment.Callback,
+ AccountServerBaseFragment.Callback {
/*
* Intent to open account settings for account=1
adb shell am start -a android.intent.action.EDIT \
@@ -113,7 +114,8 @@ public class AccountSettings extends PreferenceActivity implements FeedbackEnabl
private long mRequestedAccountId;
private Header[] mAccountListHeaders;
private Header mAppPreferencesHeader;
- /* package */ Fragment mCurrentFragment;
+ private static final String CURRENT_FRAGMENT_TAG = "currentFragment";
+ private Bundle mCurrentFragmentBundle = new Bundle(1);
private long mDeletingAccountId = -1;
private boolean mShowDebugMenu;
private List<Header> mGeneratedHeaders;
@@ -130,15 +132,6 @@ public class AccountSettings extends PreferenceActivity implements FeedbackEnabl
// Specific callbacks used by settings fragments
private final AccountSettingsFragmentCallback mAccountSettingsFragmentCallback
= new AccountSettingsFragmentCallback();
- private final AccountServerSettingsFragmentCallback mAccountServerSettingsFragmentCallback
- = new AccountServerSettingsFragmentCallback();
-
- /**
- * Display (and edit) settings for a specific account, or -1 for any/all accounts
- */
- public static void actionSettings(Activity fromActivity, long accountId) {
- fromActivity.startActivity(createAccountSettingsIntent(accountId, null, null));
- }
/**
* Create and return an intent to display (and edit) settings for a specific account, or -1
@@ -209,7 +202,8 @@ public class AccountSettings extends PreferenceActivity implements FeedbackEnabl
launchMailboxSettings(i);
return;
} else if (i.hasExtra(EXTRA_NO_ACCOUNTS)) {
- AccountSetupBasics.actionNewAccountWithResult(this);
+ final Intent setupIntent = AccountSetupFinal.actionNewAccountWithResultIntent(this);
+ startActivity(setupIntent);
finish();
return;
} else {
@@ -375,8 +369,9 @@ public class AccountSettings extends PreferenceActivity implements FeedbackEnabl
*/
@Override
public void onBackPressed() {
- if (mCurrentFragment instanceof AccountServerBaseFragment) {
- if (((AccountServerBaseFragment) mCurrentFragment).haveSettingsChanged()) {
+ final Fragment currentFragment = getCurrentFragment();
+ if (currentFragment instanceof AccountServerBaseFragment) {
+ if (((AccountServerBaseFragment) currentFragment).haveSettingsChanged()) {
UnsavedChangesDialogFragment dialogFragment =
UnsavedChangesDialogFragment.newInstanceForBack();
dialogFragment.show(getFragmentManager(), UnsavedChangesDialogFragment.TAG);
@@ -407,7 +402,8 @@ public class AccountSettings extends PreferenceActivity implements FeedbackEnabl
}
private void onAddNewAccount() {
- AccountSetupBasics.actionNewAccount(this);
+ final Intent setupIntent = AccountSetupFinal.actionNewAccountIntent(this);
+ startActivity(setupIntent);
}
/**
@@ -558,8 +554,9 @@ public class AccountSettings extends PreferenceActivity implements FeedbackEnabl
@Override
public void onHeaderClick(Header header, int position) {
// special case when exiting the server settings fragments
- if ((mCurrentFragment instanceof AccountServerBaseFragment)
- && (((AccountServerBaseFragment)mCurrentFragment).haveSettingsChanged())) {
+ final Fragment currentFragment = getCurrentFragment();
+ if ((currentFragment instanceof AccountServerBaseFragment)
+ && (((AccountServerBaseFragment)currentFragment).haveSettingsChanged())) {
UnsavedChangesDialogFragment dialogFragment =
UnsavedChangesDialogFragment.newInstanceForHeader(position);
dialogFragment.show(getFragmentManager(), UnsavedChangesDialogFragment.TAG);
@@ -586,8 +583,6 @@ public class AccountSettings extends PreferenceActivity implements FeedbackEnabl
* with a dialog, and the user OK'd it.
*/
private void forceSwitchHeader(int position) {
- // Clear the current fragment; we're navigating away
- mCurrentFragment = null;
// Ensure the UI visually shows the correct header selected
setSelection(position);
switchToHeader(mGeneratedHeaders.get(position));
@@ -597,8 +592,6 @@ public class AccountSettings extends PreferenceActivity implements FeedbackEnabl
* Forcefully go backward in the stack. This may potentially discard unsaved settings.
*/
private void forceBack() {
- // Clear the current fragment; we're navigating away
- mCurrentFragment = null;
onBackPressed();
}
@@ -609,19 +602,27 @@ public class AccountSettings extends PreferenceActivity implements FeedbackEnabl
if (f instanceof AccountSettingsFragment) {
final AccountSettingsFragment asf = (AccountSettingsFragment) f;
asf.setCallback(mAccountSettingsFragmentCallback);
- } else if (f instanceof AccountServerBaseFragment) {
- final AccountServerBaseFragment asbf = (AccountServerBaseFragment) f;
- asbf.setCallback(mAccountServerSettingsFragmentCallback);
- } else {
+ } else if (!(f instanceof AccountServerBaseFragment)) {
// Possibly uninteresting fragment, such as a dialog.
return;
}
- mCurrentFragment = f;
+ // By some internal witchcraft, this will persist a reference to this fragment across
+ // configuration changes
+ getFragmentManager().putFragment(mCurrentFragmentBundle, CURRENT_FRAGMENT_TAG, f);
// When we're changing fragments, enable/disable the add account button
invalidateOptionsMenu();
}
+ public Fragment getCurrentFragment() {
+ try {
+ return getFragmentManager().getFragment(mCurrentFragmentBundle, CURRENT_FRAGMENT_TAG);
+ } catch (final IllegalStateException e) {
+ LogUtils.d(LogUtils.TAG, e, "Could not find current fragment, returning null");
+ return null;
+ }
+ }
+
/**
* Callbacks for AccountSettingsFragment
*/
@@ -648,34 +649,90 @@ public class AccountSettings extends PreferenceActivity implements FeedbackEnabl
}
}
+ @Override
+ public void setNextButtonEnabled(boolean enabled) {}
+
/**
- * Callbacks for AccountServerSettingsFragmentCallback
+ * Save process is done, dismiss the fragment.
*/
- private class AccountServerSettingsFragmentCallback
- implements AccountServerBaseFragment.Callback {
- @Override
- public void onEnableProceedButtons(boolean enable) {
- // This is not used - it's a callback for the legacy activities
- }
+ @Override
+ public void onAccountServerSaveComplete() {
+ onBackPressed();
+ }
- @Override
- public void onProceedNext(int checkMode, AccountServerBaseFragment target) {
- AccountCheckSettingsFragment checkerFragment =
- AccountCheckSettingsFragment.newInstance(checkMode, target);
- startPreferenceFragment(checkerFragment, true);
+ @Override
+ public void onAccountServerUIComplete(int checkMode) {
+ Fragment checkerDialog = CheckSettingsProgressDialogFragment.newInstance(checkMode);
+ Fragment checkerFragment = AccountCheckSettingsFragment.newInstance(checkMode);
+ getFragmentManager().beginTransaction()
+ .add(checkerDialog, CheckSettingsProgressDialogFragment.TAG)
+ .add(checkerFragment, AccountCheckSettingsFragment.TAG)
+ .commit();
+ }
+
+ /**
+ * After verifying a new server configuration as OK, we return here and continue. This kicks
+ * off the save process.
+ */
+ @Override
+ public void onCheckSettingsComplete() {
+ dismissCheckSettingsFragment();
+ final AccountServerBaseFragment f = (AccountServerBaseFragment) getCurrentFragment();
+ f.saveSettings();
+ }
+
+ @Override
+ public void onCheckSettingsSecurityRequired(String hostName) {
+ dismissCheckSettingsFragment();
+ SecurityRequiredDialogFragment.newInstance(hostName)
+ .show(getFragmentManager(), SecurityRequiredDialogFragment.TAG);
+ }
+
+ @Override
+ public void onCheckSettingsError(int reason, String message) {
+ dismissCheckSettingsFragment();
+ CheckSettingsErrorDialogFragment.newInstance(reason, message)
+ .show(getFragmentManager(), CheckSettingsErrorDialogFragment.TAG);
+ }
+
+ @Override
+ public void onCheckSettingsAutoDiscoverComplete(int result) {
+ throw new IllegalStateException();
+ }
+
+ private void dismissCheckSettingsFragment() {
+ final Fragment f =
+ getFragmentManager().findFragmentByTag(AccountCheckSettingsFragment.TAG);
+ final Fragment d =
+ getFragmentManager().findFragmentByTag(CheckSettingsProgressDialogFragment.TAG);
+ getFragmentManager().beginTransaction()
+ .remove(f)
+ .remove(d)
+ .commit();
+ }
+
+ @Override
+ public void onSecurityRequiredDialogResult(boolean ok) {
+ if (ok) {
+ final AccountServerBaseFragment f = (AccountServerBaseFragment) getCurrentFragment();
+ f.saveSettings();
}
+ // else just stay here
+ }
- /**
- * After verifying a new server configuration as OK, we return here and continue. This
- * simply does a "back" to exit the settings screen.
- */
- @Override
- public void onCheckSettingsComplete(int result, SetupDataFragment setupData) {
- if (result == AccountCheckSettingsFragment.CHECK_SETTINGS_OK) {
- // Settings checked & saved; clear current fragment
- mCurrentFragment = null;
- onBackPressed();
- }
+ @Override
+ public void onCheckSettingsErrorDialogEditSettings() {
+ // Just stay here
+ }
+
+ @Override
+ public void onCheckSettingsErrorDialogEditCertificate() {
+ final Fragment f = getCurrentFragment();
+ if (f instanceof AccountSetupIncomingFragment) {
+ AccountSetupIncomingFragment asif = (AccountSetupIncomingFragment) f;
+ asif.onCertificateRequested();
+ } else {
+ LogUtils.wtf(LogUtils.TAG, "Tried to change cert on non-incoming screen?");
}
}
@@ -716,8 +773,7 @@ public class AccountSettings extends PreferenceActivity implements FeedbackEnabl
public void onIncomingSettings(Account account) {
try {
mSetupData = new SetupDataFragment(SetupDataFragment.FLOW_MODE_EDIT, account);
- final Fragment f = new AccountSetupIncomingFragment();
- f.setArguments(AccountSetupIncomingFragment.getArgs(true));
+ final Fragment f = AccountSetupIncomingFragment.newInstance(true);
// Use startPreferenceFragment here because we need to keep this activity instance
startPreferenceFragment(f, true);
} catch (Exception e) {
@@ -727,14 +783,11 @@ public class AccountSettings extends PreferenceActivity implements FeedbackEnabl
/**
* Dispatch to edit outgoing settings.
- *
- * TODO: Make things less hardwired
*/
public void onOutgoingSettings(Account account) {
try {
mSetupData = new SetupDataFragment(SetupDataFragment.FLOW_MODE_EDIT, account);
- final Fragment f = new AccountSetupOutgoingFragment();
- f.setArguments(AccountSetupOutgoingFragment.getArgs(true));
+ final Fragment f = AccountSetupOutgoingFragment.newInstance(true);
// Use startPreferenceFragment here because we need to keep this activity instance
startPreferenceFragment(f, true);
} catch (Exception e) {
@@ -939,9 +992,4 @@ public class AccountSettings extends PreferenceActivity implements FeedbackEnabl
public SetupDataFragment getSetupData() {
return mSetupData;
}
-
- @Override
- public void setSetupData(SetupDataFragment setupData) {
- mSetupData = setupData;
- }
}
diff --git a/src/com/android/email/activity/setup/AccountSetupActivity.java b/src/com/android/email/activity/setup/AccountSetupActivity.java
index fd6e63da0..1f6ab4e9f 100644
--- a/src/com/android/email/activity/setup/AccountSetupActivity.java
+++ b/src/com/android/email/activity/setup/AccountSetupActivity.java
@@ -20,15 +20,11 @@ import android.app.Activity;
import android.app.FragmentTransaction;
import android.os.Bundle;
-import com.android.emailcommon.Logging;
-import com.android.mail.utils.LogUtils;
-
/**
* Superclass of all of the account setup activities; ensures that SetupData state is saved/restored
* automatically as required
*/
public class AccountSetupActivity extends Activity implements SetupDataFragment.SetupDataContainer {
- private static final boolean DEBUG_SETUP_FLOWS = false; // Don't check in set to true
protected SetupDataFragment mSetupData;
private static final String SETUP_DATA_FRAGMENT_TAG = "setupData";
@@ -57,20 +53,10 @@ public class AccountSetupActivity extends Activity implements SetupDataFragment.
ft.add(mSetupData, SETUP_DATA_FRAGMENT_TAG);
ft.commit();
}
-
- if (DEBUG_SETUP_FLOWS) {
- LogUtils.d(Logging.LOG_TAG, "%s onCreate %s", getClass().getName(),
- mSetupData.debugString());
- }
}
@Override
public SetupDataFragment getSetupData() {
return mSetupData;
}
-
- @Override
- public void setSetupData(SetupDataFragment setupData) {
- mSetupData = setupData;
- }
}
diff --git a/src/com/android/email/activity/setup/AccountSetupBasics.java b/src/com/android/email/activity/setup/AccountSetupBasics.java
deleted file mode 100644
index 89ba7a112..000000000
--- a/src/com/android/email/activity/setup/AccountSetupBasics.java
+++ /dev/null
@@ -1,642 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.email.activity.setup;
-
-import android.accounts.AccountAuthenticatorResponse;
-import android.accounts.AccountManager;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.FragmentTransaction;
-import android.app.LoaderManager;
-import android.content.Context;
-import android.content.CursorLoader;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.Loader;
-import android.database.Cursor;
-import android.graphics.Paint;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.email.R;
-import com.android.email.activity.UiUtilities;
-import com.android.email.service.EmailServiceUtils;
-import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
-import com.android.emailcommon.Logging;
-import com.android.emailcommon.VendorPolicyLoader.Provider;
-import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.Credential;
-import com.android.emailcommon.provider.EmailContent;
-import com.android.emailcommon.provider.HostAuth;
-import com.android.emailcommon.utility.Utility;
-import com.android.mail.utils.LogUtils;
-
-import java.net.URISyntaxException;
-
-/**
- * Prompts the user for the email address and password. Also prompts for "Use this account as
- * default" if this is the 2nd+ account being set up.
- *
- * If the domain is well-known, the account is configured fully and checked immediately
- * using AccountCheckSettingsFragment. If this succeeds we proceed directly to AccountSetupOptions.
- *
- * If the domain is not known, or the user selects Manual setup, we invoke the
- * AccountSetupAccountType activity where the user can begin to manually configure the account.
- *
- * === Support for automated testing ==
- * This activity can also be launched directly via INTENT_CREATE_ACCOUNT. This is intended
- * only for use by continuous test systems, and is currently only available when
- * {@link ActivityManager#isRunningInTestHarness()} is set. To use this mode, you must construct
- * an intent which contains all necessary information to create the account. No connection
- * checking is done, so the account may or may not actually work. Here is a sample command, for a
- * gmail account "test_account" with a password of "test_password".
- *
- * $ adb shell am start -a com.android.email.CREATE_ACCOUNT \
- * -e EMAIL test_account@gmail.com \
- * -e USER "Test Account Name" \
- * -e INCOMING imap+ssl+://test_account:test_password@imap.gmail.com \
- * -e OUTGOING smtp+ssl+://test_account:test_password@smtp.gmail.com
- *
- * Note: For accounts that require the full email address in the login, encode the @ as %40.
- * Note: Exchange accounts that require device security policies cannot be created automatically.
- */
-public class AccountSetupBasics extends AccountSetupActivity
- implements OnClickListener {
-
- // STOPSHIP: Set to false before shipping, logs PII
- private final static boolean ENTER_DEBUG_SCREEN = true;
-
- /**
- * Direct access for forcing account creation
- * For use by continuous automated test system (e.g. in conjunction with monkey tests)
- */
- private static String INTENT_CREATE_ACCOUNT;
- private static final String EXTRA_FLOW_MODE = "FLOW_MODE";
- private static final String EXTRA_FLOW_ACCOUNT_TYPE = "FLOW_ACCOUNT_TYPE";
- private static final String EXTRA_CREATE_ACCOUNT_EMAIL = "EMAIL";
- private static final String EXTRA_CREATE_ACCOUNT_USER = "USER";
- private static final String EXTRA_CREATE_ACCOUNT_INCOMING = "INCOMING";
- private static final String EXTRA_CREATE_ACCOUNT_OUTGOING = "OUTGOING";
- private static final Boolean DEBUG_ALLOW_NON_TEST_HARNESS_CREATION = false;
-
- private static final String STATE_KEY_PROVIDER = "AccountSetupBasics.provider";
-
- // Support for UI
- private Provider mProvider;
- private TextView mManualButton;
- private ImageButton mNextButton;
- private boolean mNextButtonInhibit;
- private boolean mPaused;
-
- private static final int OWNER_NAME_LOADER_ID = 0;
- private String mOwnerName;
-
- public static void actionNewAccount(Activity fromActivity) {
- final Intent i = new Intent(fromActivity, AccountSetupBasics.class);
- i.putExtra(EXTRA_FLOW_MODE, SetupDataFragment.FLOW_MODE_NORMAL);
- fromActivity.startActivity(i);
- }
-
- public static void actionNewAccountWithResult(Activity fromActivity) {
- final Intent i = new ForwardingIntent(fromActivity, AccountSetupBasics.class);
- i.putExtra(EXTRA_FLOW_MODE, SetupDataFragment.FLOW_MODE_NO_ACCOUNTS);
- fromActivity.startActivity(i);
- }
-
- /**
- * This generates setup data that can be used to start a self-contained account creation flow
- * for exchange accounts.
- */
- public static Intent actionGetCreateAccountIntent(Context context, String accountManagerType) {
- final Intent i = new Intent(context, AccountSetupBasics.class);
- i.putExtra(EXTRA_FLOW_MODE, SetupDataFragment.FLOW_MODE_ACCOUNT_MANAGER);
- i.putExtra(EXTRA_FLOW_ACCOUNT_TYPE, accountManagerType);
- return i;
- }
-
- public static void actionAccountCreateFinishedAccountFlow(Activity fromActivity) {
- final Intent i= new ForwardingIntent(fromActivity, AccountSetupBasics.class);
- // If we're in the "account flow" (from AccountManager), we want to return to the caller
- // (in the settings app)
- i.putExtra(SetupDataFragment.EXTRA_SETUP_DATA,
- new SetupDataFragment(SetupDataFragment.FLOW_MODE_RETURN_TO_CALLER));
- i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- fromActivity.startActivity(i);
- }
-
- public static void actionAccountCreateFinishedWithResult(Activity fromActivity) {
- final Intent i= new ForwardingIntent(fromActivity, AccountSetupBasics.class);
- // If we're in the "no accounts" flow, we want to return to the caller with a result
- i.putExtra(SetupDataFragment.EXTRA_SETUP_DATA,
- new SetupDataFragment(SetupDataFragment.FLOW_MODE_RETURN_NO_ACCOUNTS_RESULT));
- i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- fromActivity.startActivity(i);
- }
-
- public static void actionAccountCreateFinished(final Activity fromActivity, Account account) {
- final Intent i = new Intent(fromActivity, AccountSetupBasics.class);
- // If we're not in the "account flow" (from AccountManager), we want to show the
- // message list for the new inbox
- i.putExtra(SetupDataFragment.EXTRA_SETUP_DATA,
- new SetupDataFragment(SetupDataFragment.FLOW_MODE_RETURN_TO_MESSAGE_LIST, account));
- i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- fromActivity.startActivity(i);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // Check for forced account creation first, as it comes from an externally-generated
- // intent and won't have any SetupData prepared.
- final Intent intent = getIntent();
- final String action = intent.getAction();
-
- if (INTENT_CREATE_ACCOUNT == null) {
- INTENT_CREATE_ACCOUNT = getString(R.string.intent_create_account);
- }
- if (INTENT_CREATE_ACCOUNT.equals(action)) {
- mSetupData = new SetupDataFragment(SetupDataFragment.FLOW_MODE_FORCE_CREATE);
- } else {
- final int intentFlowMode =
- intent.getIntExtra(EXTRA_FLOW_MODE, SetupDataFragment.FLOW_MODE_UNSPECIFIED);
- if (intentFlowMode != SetupDataFragment.FLOW_MODE_UNSPECIFIED) {
- mSetupData = new SetupDataFragment(intentFlowMode,
- intent.getStringExtra(EXTRA_FLOW_ACCOUNT_TYPE));
- }
- }
-
- final int flowMode = mSetupData.getFlowMode();
- if (flowMode == SetupDataFragment.FLOW_MODE_RETURN_TO_CALLER) {
- // Return to the caller who initiated account creation
- finish();
- return;
- } else if (flowMode == SetupDataFragment.FLOW_MODE_RETURN_NO_ACCOUNTS_RESULT) {
- if (EmailContent.count(this, Account.CONTENT_URI) > 0) {
- setResult(RESULT_OK);
- } else {
- setResult(RESULT_CANCELED);
- }
- finish();
- return;
- } else if (flowMode == SetupDataFragment.FLOW_MODE_RETURN_TO_MESSAGE_LIST) {
- final Account account = mSetupData.getAccount();
- if (account != null && account.mId >= 0) {
- // Show the message list for the new account
- //***
- //Welcome.actionOpenAccountInbox(this, account.mId);
- finish();
- return;
- }
- }
-
- setContentView(R.layout.account_setup_basics);
-
- // Configure buttons
- mManualButton = UiUtilities.getView(this, R.id.manual_setup);
- mNextButton = UiUtilities.getView(this, R.id.next);
- mManualButton.setVisibility(View.VISIBLE);
- mManualButton.setOnClickListener(this);
- mNextButton.setOnClickListener(this);
-
- mManualButton.setPaintFlags(mManualButton.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
-
- // Force disabled until validator notifies otherwise
- setProceedButtonsEnabled(false);
- // Lightweight debounce while Async tasks underway
- mNextButtonInhibit = false;
-
- // Set aside incoming AccountAuthenticatorResponse, if there was any
- final AccountAuthenticatorResponse authenticatorResponse =
- getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
- mSetupData.setAccountAuthenticatorResponse(authenticatorResponse);
- if (authenticatorResponse != null) {
- // When this Activity is called as part of account authentification flow,
- // we are responsible for eventually reporting the result (success or failure) to
- // the account manager. Most exit paths represent an failed or abandoned setup,
- // so the default is to report the error. Success will be reported by the code in
- // AccountSetupOptions that commits the finally created account.
- mSetupData.setReportAccountAuthenticationError(true);
- }
-
- // Handle force account creation immediately (now that fragment is set up)
- // This is never allowed in a normal user build and will exit immediately.
- if (mSetupData.getFlowMode() == SetupDataFragment.FLOW_MODE_FORCE_CREATE) {
- if (!DEBUG_ALLOW_NON_TEST_HARNESS_CREATION &&
- !ActivityManager.isRunningInTestHarness()) {
- LogUtils.e(Logging.LOG_TAG,
- "ERROR: Force account create only allowed while in test harness");
- finish();
- return;
- }
- final String email = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_EMAIL);
- final String user = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_USER);
- final String incoming = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_INCOMING);
- final String outgoing = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_OUTGOING);
- if (TextUtils.isEmpty(email) || TextUtils.isEmpty(user) ||
- TextUtils.isEmpty(incoming) || TextUtils.isEmpty(outgoing)) {
- LogUtils.e(Logging.LOG_TAG, "ERROR: Force account create requires extras EMAIL, " +
- "USER, INCOMING, OUTGOING");
- finish();
- return;
- }
- forceCreateAccount(email, user, incoming, outgoing);
- // calls finish
- // XXX disabled for now, we don't finish account setup in this activity anymore.
- // onCheckSettingsComplete(AccountCheckSettingsFragment.CHECK_SETTINGS_OK, mSetupData);
- return;
- }
-
- if (savedInstanceState != null && savedInstanceState.containsKey(STATE_KEY_PROVIDER)) {
- mProvider = (Provider) savedInstanceState.getSerializable(STATE_KEY_PROVIDER);
- }
-
- // Launch a loader to look up the owner name. It should be ready well in advance of
- // the time the user clicks next or manual.
- getLoaderManager().initLoader(OWNER_NAME_LOADER_ID, null,
- new LoaderManager.LoaderCallbacks<Cursor>() {
- @Override
- public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
- return new CursorLoader(AccountSetupBasics.this,
- ContactsContract.Profile.CONTENT_URI,
- new String[] {ContactsContract.Profile.DISPLAY_NAME_PRIMARY},
- null, null, null);
- }
-
- @Override
- public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) {
- if (data != null && data.moveToFirst()) {
- mOwnerName = data.getString(data.getColumnIndex(
- ContactsContract.Profile.DISPLAY_NAME_PRIMARY));
- }
- }
-
- @Override
- public void onLoaderReset(final Loader<Cursor> loader) {
- }
- });
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mPaused = true;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mPaused = false;
- }
-
- @Override
- public void finish() {
- // If the account manager initiated the creation, and success was not reported,
- // then we assume that we're giving up (for any reason) - report failure.
- if (mSetupData.getReportAccountAuthenticationError()) {
- final AccountAuthenticatorResponse authenticatorResponse =
- mSetupData.getAccountAuthenticatorResponse();
- if (authenticatorResponse != null) {
- authenticatorResponse.onError(AccountManager.ERROR_CODE_CANCELED, "canceled");
- mSetupData.setAccountAuthenticatorResponse(null);
- }
- }
- super.finish();
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- if (mProvider != null) {
- outState.putSerializable(STATE_KEY_PROVIDER, mProvider);
- }
- }
-
- /**
- * Implements OnClickListener
- */
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.next:
- // Simple debounce - just ignore while async checks are underway
- if (mNextButtonInhibit) {
- return;
- }
- onNext();
- break;
- case R.id.manual_setup:
- onManualSetup(false);
- break;
- }
- }
-
- /**
- * Return an existing username if found, or null. This is the result of the Callable (below).
- */
- private String getOwnerName() {
- return mOwnerName;
- }
-
- /**
- * Finish the auto setup process, in some cases after showing a warning dialog.
- */
- private void finishAutoSetup() {
-
- final AccountSetupBasicsFragment basicsFragment = getBasicsFragment();
- final String email = basicsFragment.getEmail();
-
- try {
- mProvider.expandTemplates(email);
-
- final Account account = mSetupData.getAccount();
- final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
- HostAuth.setHostAuthFromString(recvAuth, mProvider.incomingUri);
- recvAuth.setLogin(mProvider.incomingUsername, null);
-
- final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(this,
- recvAuth.mProtocol);
- recvAuth.mPort =
- ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) ? info.portSsl : info.port;
-
- final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
- HostAuth.setHostAuthFromString(sendAuth, mProvider.outgoingUri);
- sendAuth.setLogin(mProvider.outgoingUsername, null);
-
- // Populate the setup data, assuming that the duplicate account check will succeed
- populateSetupData(getOwnerName(), email);
-
- // Stop here if the login credentials duplicate an existing account
- // Launch an Async task to do the work
- new DuplicateCheckTask(this, email, true)
- .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } catch (URISyntaxException e) {
- /*
- * If there is some problem with the URI we give up and go on to manual setup.
- * Technically speaking, AutoDiscover is OK here, since the user clicked "Next"
- * to get here. This will not happen in practice because we don't expect to
- * find any EAS accounts in the providers list.
- */
- onManualSetup(true);
- }
- }
-
- /**
- * Async task that continues the work of finishAutoSetup(). Checks for a duplicate
- * account and then either alerts the user, or continues.
- */
- private class DuplicateCheckTask extends AsyncTask<Void, Void, String> {
- private final Context mContext;
- private final String mCheckAddress;
- private final boolean mAutoSetup;
-
- public DuplicateCheckTask(Context context, String checkAddress,
- boolean autoSetup) {
- mContext = context;
- mCheckAddress = checkAddress;
- // Prevent additional clicks on the next button during Async lookup
- mNextButtonInhibit = true;
- mAutoSetup = autoSetup;
- }
-
- @Override
- protected String doInBackground(Void... params) {
- return Utility.findExistingAccount(mContext, null, mCheckAddress);
- }
-
- @Override
- protected void onPostExecute(String duplicateAccountName) {
- mNextButtonInhibit = false;
- // Exit immediately if the user left before we finished
- if (mPaused) return;
- // Show duplicate account warning, or proceed.
- if (duplicateAccountName != null) {
- final DuplicateAccountDialogFragment dialogFragment =
- DuplicateAccountDialogFragment.newInstance(duplicateAccountName);
- dialogFragment.show(getFragmentManager(), DuplicateAccountDialogFragment.TAG);
- } else {
- if (mAutoSetup) {
- final Intent intent = new Intent(AccountSetupBasics.this, SignInActivity.class);
- intent.putExtra(SignInActivity.EXTRA_FLOW_MODE_INITIAL, true);
- intent.putExtra(SignInActivity.EXTRA_MANUAL_SETUP, false);
- intent.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, mSetupData);
- startActivity(intent);
- } else {
- onManualSetup(true);
- }
- }
- }
-
- @Override
- protected void onCancelled(String s) {
- mNextButtonInhibit = false;
- LogUtils.d(LogUtils.TAG, "DuplicateCheckTask cancelled (AccountSetupBasics)");
- }
- }
-
- /**
- * When "next" button is clicked
- */
- private void onNext() {
- // Try auto-configuration from XML providers (unless in EAS mode, we can skip it)
- final String email = getBasicsFragment().getEmail();
- final String[] emailParts = email.split("@");
- final String domain = emailParts[1].trim();
- mProvider = AccountSettingsUtils.findProviderForDomain(this, domain);
- if (mProvider != null) {
- mProvider.expandTemplates(email);
- if (mProvider.note != null) {
- final NoteDialogFragment dialogFragment =
- NoteDialogFragment.newInstance(mProvider.note);
- dialogFragment.show(getFragmentManager(), NoteDialogFragment.TAG);
- } else {
- finishAutoSetup();
- }
- } else {
- // Can't use auto setup (although EAS accounts may still be able to AutoDiscover)
- new DuplicateCheckTask(this, email, false)
- .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
- }
-
- /**
- * When "manual setup" button is clicked
- *
- * @param allowAutoDiscover - true if the user clicked 'next' and (if the account is EAS)
- * it's OK to use autodiscover. false to prevent autodiscover and go straight to manual setup.
- * Ignored for IMAP & POP accounts.
- */
- private void onManualSetup(boolean allowAutoDiscover) {
- final AccountSetupBasicsFragment basicsFragment = getBasicsFragment();
- final String email = basicsFragment.getEmail();
- final String[] emailParts = email.split("@");
- final String user = emailParts[0].trim();
- final String domain = emailParts[1].trim();
-
- // Alternate entry to the debug options screen (for devices without a physical keyboard:
- // Username: d@d.d
- // Password: debug
- if (ENTER_DEBUG_SCREEN) {
- if ("d@d.d".equals(email)) {
- basicsFragment.setEmail("");
- AccountSettings.actionSettingsWithDebug(this);
- return;
- }
- }
-
- final Account account = mSetupData.getAccount();
- final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
- recvAuth.setConnection(null, domain, HostAuth.PORT_UNKNOWN, HostAuth.FLAG_NONE);
- recvAuth.setLogin(user, null);
-
- final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
- sendAuth.setConnection(null, domain, HostAuth.PORT_UNKNOWN, HostAuth.FLAG_NONE);
- sendAuth.setLogin(user, null);
-
- populateSetupData(getOwnerName(), email);
-
- mSetupData.setAllowAutodiscover(allowAutoDiscover);
-
- // FLAG: We should not launch the protocol picker if we are coming from device settings,
- // (as opposed to in-app adding account.) If we come from device settings, the user has
- // already explicitly chosen the account type.
- AccountSetupType.actionSelectAccountType(this, mSetupData);
- }
-
- /**
- * To support continuous testing, we allow the forced creation of accounts.
- * This works in a manner fairly similar to automatic setup, in which the complete server
- * Uri's are available, except that we will also skip checking (as if both checks were true)
- * and all other UI.
- *
- * @param email The email address for the new account
- * @param user The user name for the new account
- * @param incoming The URI-style string defining the incoming account
- * @param outgoing The URI-style string defining the outgoing account
- */
- private void forceCreateAccount(String email, String user, String incoming, String outgoing) {
- Account account = mSetupData.getAccount();
- try {
- final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
- HostAuth.setHostAuthFromString(recvAuth, incoming);
-
- final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
- HostAuth.setHostAuthFromString(sendAuth, outgoing);
-
- populateSetupData(user, email);
- } catch (URISyntaxException e) {
- // If we can't set up the URL, don't continue - account setup pages will fail too
- Toast.makeText(
- this, R.string.account_setup_username_password_toast, Toast.LENGTH_LONG).show();
- }
- }
-
- public static void setDefaultsForProtocol(Context context, Account account) {
- final String protocol = account.mHostAuthRecv.mProtocol;
- if (protocol == null) return;
- final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(context, protocol);
- account.mSyncInterval = info.defaultSyncInterval;
- account.mSyncLookback = info.defaultLookback;
- if (info.offerLocalDeletes) {
- account.setDeletePolicy(info.defaultLocalDeletes);
- }
- }
-
- /**
- * Populate SetupData's account with complete setup info.
- */
- private void populateSetupData(String senderName, String senderEmail) {
- final Account account = mSetupData.getAccount();
- account.setSenderName(senderName);
- account.setEmailAddress(senderEmail);
- account.setDisplayName(senderEmail);
- setDefaultsForProtocol(this, account);
- }
-
- public void setProceedButtonsEnabled(boolean enabled) {
- mManualButton.setEnabled(enabled);
- mNextButton.setEnabled(enabled);
- }
-
- /**
- * Dialog fragment to show "setup note" dialog
- */
- public static class NoteDialogFragment extends DialogFragment {
- final static String TAG = "NoteDialogFragment";
-
- // Argument bundle keys
- private final static String BUNDLE_KEY_NOTE = "NoteDialogFragment.Note";
-
- // Public no-args constructor needed for fragment re-instantiation
- public NoteDialogFragment() {}
-
- /**
- * Create the dialog with parameters
- */
- public static NoteDialogFragment newInstance(String note) {
- final NoteDialogFragment f = new NoteDialogFragment();
- final Bundle b = new Bundle(1);
- b.putString(BUNDLE_KEY_NOTE, note);
- f.setArguments(b);
- return f;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Context context = getActivity();
- final String note = getArguments().getString(BUNDLE_KEY_NOTE);
-
- return new AlertDialog.Builder(context)
- .setIconAttribute(android.R.attr.alertDialogIcon)
- .setTitle(android.R.string.dialog_alert_title)
- .setMessage(note)
- .setPositiveButton(
- R.string.okay_action,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final Activity a = getActivity();
- if (a instanceof AccountSetupBasics) {
- ((AccountSetupBasics)a).finishAutoSetup();
- }
- dismiss();
- }
- })
- .setNegativeButton(
- context.getString(R.string.cancel_action),
- null)
- .create();
- }
- }
-
- private AccountSetupBasicsFragment getBasicsFragment() {
- return (AccountSetupBasicsFragment)
- getFragmentManager().findFragmentById(R.id.basics_fragment);
- }
-}
diff --git a/src/com/android/email/activity/setup/AccountSetupBasicsFragment.java b/src/com/android/email/activity/setup/AccountSetupBasicsFragment.java
index fd793f2dd..23d892ccb 100644
--- a/src/com/android/email/activity/setup/AccountSetupBasicsFragment.java
+++ b/src/com/android/email/activity/setup/AccountSetupBasicsFragment.java
@@ -16,7 +16,6 @@
package com.android.email.activity.setup;
-import android.app.Fragment;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
@@ -30,17 +29,29 @@ import com.android.email.R;
import com.android.email.activity.UiUtilities;
import com.android.emailcommon.mail.Address;
-public class AccountSetupBasicsFragment extends Fragment {
+public class AccountSetupBasicsFragment extends AccountSetupFragment implements
+ View.OnClickListener {
private EditText mEmailView;
+ public interface Callback extends AccountSetupFragment.Callback {
+ void onBasicsManualSetupButton();
+ }
+
+ public static AccountSetupBasicsFragment newInstance() {
+ return new AccountSetupBasicsFragment();
+ }
+
public AccountSetupBasicsFragment() {}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- final View view = inflater.inflate(R.layout.account_setup_basics_fragment, container, false);
+ final View view = inflater.inflate(R.layout.account_setup_basics_fragment, container,
+ false);
mEmailView = UiUtilities.getView(view, R.id.account_email);
+ final View manualSetupButton = UiUtilities.getView(view, R.id.manual_setup);
+ manualSetupButton.setOnClickListener(this);
final TextWatcher textWatcher = new TextWatcher() {
@Override
@@ -60,8 +71,13 @@ public class AccountSetupBasicsFragment extends Fragment {
return view;
}
+ @Override
+ public void onViewStateRestored(Bundle savedInstanceState) {
+ super.onViewStateRestored(savedInstanceState);
+ validateFields();
+ }
+
private void validateFields() {
- final AccountSetupBasics activity = (AccountSetupBasics) getActivity();
final String emailField = getEmail();
final Address[] addresses = Address.parse(emailField);
@@ -69,7 +85,16 @@ public class AccountSetupBasicsFragment extends Fragment {
&& addresses.length == 1
&& !TextUtils.isEmpty(addresses[0].getAddress());
- activity.setProceedButtonsEnabled(emailValid);
+ final Callback callback = (Callback) getActivity();
+ callback.setNextButtonEnabled(emailValid);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.manual_setup) {
+ Callback callback = (Callback) getActivity();
+ callback.onBasicsManualSetupButton();
+ }
}
public void setEmail(final String email) {
diff --git a/src/com/android/email/activity/setup/AccountSetupCredentialsFragment.java b/src/com/android/email/activity/setup/AccountSetupCredentialsFragment.java
new file mode 100644
index 000000000..4955fed8d
--- /dev/null
+++ b/src/com/android/email/activity/setup/AccountSetupCredentialsFragment.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.email.activity.setup;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.text.format.DateUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+import com.android.email.R;
+import com.android.email.activity.UiUtilities;
+import com.android.email.service.EmailServiceUtils;
+import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
+import com.android.emailcommon.VendorPolicyLoader.OAuthProvider;
+import com.android.emailcommon.provider.Credential;
+import com.android.emailcommon.provider.HostAuth;
+import com.android.mail.utils.LogUtils;
+
+import java.util.List;
+
+public class AccountSetupCredentialsFragment extends AccountSetupFragment
+ implements OnClickListener {
+ private static final String EXTRA_EMAIL = "email";
+ private static final String EXTRA_PROTOCOL = "protocol";
+
+ public static final String EXTRA_PASSWORD = "password";
+ public static final String EXTRA_OAUTH_PROVIDER = "provider";
+ public static final String EXTRA_OAUTH_ACCESS_TOKEN = "accessToken";
+ public static final String EXTRA_OAUTH_REFRESH_TOKEN = "refreshToken";
+ public static final String EXTRA_OAUTH_EXPIRES_IN_SECONDS = "expiresInSeconds";
+
+ private View mOAuthGroup;
+ private View mOAuthButton;
+ private EditText mImapPasswordText;
+ private EditText mRegularPasswordText;
+ private TextWatcher mValidationTextWatcher;
+ private String mEmailAddress;
+ private boolean mOfferOAuth;
+ private String mProviderId;
+ private Context mAppContext;
+ private Bundle mResults;
+
+ public interface Callback extends AccountSetupFragment.Callback {
+ void onCredentialsComplete(Bundle results);
+ }
+
+ /**
+ * Create a new instance of this fragment with the appropriate email and protocol
+ * @param email login address for OAuth purposes
+ * @param protocol protocol of the service we're gathering credentials for
+ * @return new fragment instance
+ */
+ public static AccountSetupCredentialsFragment newInstance(final String email,
+ final String protocol) {
+ final AccountSetupCredentialsFragment f = new AccountSetupCredentialsFragment();
+ final Bundle b = new Bundle(2);
+ b.putString(EXTRA_EMAIL, email);
+ b.putString(EXTRA_PROTOCOL, protocol);
+ f.setArguments(b);
+ return f;
+ }
+
+ @Override
+ public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
+ final Bundle savedInstanceState) {
+ final View view = inflater.inflate(R.layout.account_setup_credentials_fragment, container,
+ false);
+
+ mImapPasswordText = UiUtilities.getView(view, R.id.imap_password);
+ mRegularPasswordText = UiUtilities.getView(view, R.id.regular_password);
+ mOAuthGroup = UiUtilities.getView(view, R.id.oauth_group);
+ mOAuthButton = UiUtilities.getView(view, R.id.sign_in_with_google);
+ mOAuthButton.setOnClickListener(this);
+
+ // After any text edits, call validateFields() which enables or disables the Next button
+ mValidationTextWatcher = new TextWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ validatePassword();
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) { }
+ };
+ mImapPasswordText.addTextChangedListener(mValidationTextWatcher);
+ mRegularPasswordText.addTextChangedListener(mValidationTextWatcher);
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(final Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mAppContext = getActivity().getApplicationContext();
+ mEmailAddress = getArguments().getString(EXTRA_EMAIL);
+ final String protocol = getArguments().getString(EXTRA_PROTOCOL);
+ if (protocol != null) {
+ final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(mAppContext, protocol);
+ mOfferOAuth = info.offerOAuth;
+ } else {
+ // TODO: for now, we might not know what protocol we're using, so just default to
+ // offering oauth
+ mOfferOAuth = true;
+ }
+ if (mOfferOAuth) {
+ mOAuthGroup.setVisibility(View.VISIBLE);
+ mRegularPasswordText.setVisibility(View.GONE);
+ } else {
+ mOAuthGroup.setVisibility(View.GONE);
+ mRegularPasswordText.setVisibility(View.VISIBLE);
+ }
+ validatePassword();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mImapPasswordText.removeTextChangedListener(mValidationTextWatcher);
+ mImapPasswordText = null;
+ mRegularPasswordText.removeTextChangedListener(mValidationTextWatcher);
+ mRegularPasswordText = null;
+ }
+
+ public void validatePassword() {
+ final Callback callback = (Callback) getActivity();
+ if (callback != null) {
+ callback.setNextButtonEnabled(!TextUtils.isEmpty(getPassword()));
+ }
+ // Warn (but don't prevent) if password has leading/trailing spaces
+ AccountSettingsUtils.checkPasswordSpaces(mAppContext, mImapPasswordText);
+ AccountSettingsUtils.checkPasswordSpaces(mAppContext, mRegularPasswordText);
+ }
+
+ @Override
+ public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
+ if (requestCode == OAuthAuthenticationActivity.REQUEST_OAUTH) {
+ if (resultCode == OAuthAuthenticationActivity.RESULT_OAUTH_SUCCESS) {
+ final String accessToken = data.getStringExtra(
+ OAuthAuthenticationActivity.EXTRA_OAUTH_ACCESS_TOKEN);
+ final String refreshToken = data.getStringExtra(
+ OAuthAuthenticationActivity.EXTRA_OAUTH_REFRESH_TOKEN);
+ final int expiresInSeconds = data.getIntExtra(
+ OAuthAuthenticationActivity.EXTRA_OAUTH_EXPIRES_IN, 0);
+ final Bundle results = new Bundle(4);
+ results.putString(EXTRA_OAUTH_PROVIDER, mProviderId);
+ results.putString(EXTRA_OAUTH_ACCESS_TOKEN, accessToken);
+ results.putString(EXTRA_OAUTH_REFRESH_TOKEN, refreshToken);
+ results.putInt(EXTRA_OAUTH_EXPIRES_IN_SECONDS, expiresInSeconds);
+ mResults = results;
+ final Callback callback = (Callback) getActivity();
+ callback.onCredentialsComplete(results);
+ } else if (resultCode == OAuthAuthenticationActivity.RESULT_OAUTH_FAILURE
+ || resultCode == OAuthAuthenticationActivity.RESULT_OAUTH_USER_CANCELED) {
+ LogUtils.i(LogUtils.TAG, "Result from oauth %d", resultCode);
+ } else {
+ LogUtils.wtf(LogUtils.TAG, "Unknown result code from OAUTH: %d", resultCode);
+ }
+ } else {
+ LogUtils.e(LogUtils.TAG, "Unknown request code for onActivityResult in"
+ + " AccountSetupBasics: %d", requestCode);
+ }
+ }
+
+ @Override
+ public void onClick(final View view) {
+ if (view == mOAuthButton) {
+ List<OAuthProvider> oauthProviders = AccountSettingsUtils.getAllOAuthProviders(
+ mAppContext);
+ // TODO currently the only oauth provider we support is google.
+ // If we ever have more than 1 oauth provider, then we need to implement some sort
+ // of picker UI. For now, just always take the first oauth provider.
+ if (oauthProviders.size() > 0) {
+ mProviderId = oauthProviders.get(0).id;
+ final Intent i = new Intent(getActivity(), OAuthAuthenticationActivity.class);
+ i.putExtra(OAuthAuthenticationActivity.EXTRA_EMAIL_ADDRESS, mEmailAddress);
+ i.putExtra(OAuthAuthenticationActivity.EXTRA_PROVIDER, mProviderId);
+ startActivityForResult(i, OAuthAuthenticationActivity.REQUEST_OAUTH);
+ }
+ }
+ }
+
+ public String getPassword() {
+ if (mOfferOAuth) {
+ return mImapPasswordText.getText().toString();
+ } else {
+ return mRegularPasswordText.getText().toString();
+ }
+ }
+
+ public Bundle getCredentialResults() {
+ if (mResults != null) {
+ return mResults;
+ }
+
+ final Bundle results = new Bundle(1);
+ results.putString(EXTRA_PASSWORD, getPassword());
+ return results;
+ }
+
+ public static void populateHostAuthWithResults(final Context context, final HostAuth hostAuth,
+ final Bundle results) {
+ if (results == null) {
+ return;
+ }
+ final String password = results.getString(AccountSetupCredentialsFragment.EXTRA_PASSWORD);
+ if (!TextUtils.isEmpty(password)) {
+ hostAuth.mPassword = password;
+ hostAuth.removeCredential();
+ } else {
+ Credential cred = hostAuth.getOrCreateCredential(context);
+ cred.mProviderId = results.getString(
+ AccountSetupCredentialsFragment.EXTRA_OAUTH_PROVIDER);
+ cred.mAccessToken = results.getString(
+ AccountSetupCredentialsFragment.EXTRA_OAUTH_ACCESS_TOKEN);
+ cred.mRefreshToken = results.getString(
+ AccountSetupCredentialsFragment.EXTRA_OAUTH_REFRESH_TOKEN);
+ cred.mExpiration = System.currentTimeMillis()
+ + results.getInt(
+ AccountSetupCredentialsFragment.EXTRA_OAUTH_EXPIRES_IN_SECONDS, 0)
+ * DateUtils.SECOND_IN_MILLIS;
+ hostAuth.mPassword = null;
+ }
+ }
+}
diff --git a/src/com/android/email/activity/setup/AccountSetupFinal.java b/src/com/android/email/activity/setup/AccountSetupFinal.java
index 04bc80893..68a9b435a 100644
--- a/src/com/android/email/activity/setup/AccountSetupFinal.java
+++ b/src/com/android/email/activity/setup/AccountSetupFinal.java
@@ -18,7 +18,7 @@ package com.android.email.activity.setup;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
-import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@@ -26,66 +26,227 @@ import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.LoaderManager;
import android.app.ProgressDialog;
-import android.content.ContentValues;
import android.content.Context;
+import android.content.CursorLoader;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
+import android.database.Cursor;
import android.os.Bundle;
+import android.provider.ContactsContract;
import android.text.TextUtils;
import android.view.View;
-import android.widget.Button;
import android.widget.ImageButton;
import android.widget.TextView;
+import android.widget.Toast;
import com.android.email.R;
import com.android.email.activity.UiUtilities;
-import com.android.email.provider.AccountBackupRestore;
import com.android.email.service.EmailServiceUtils;
+import com.android.emailcommon.VendorPolicyLoader;
import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.EmailContent;
-import com.android.mail.ui.MailAsyncTaskLoader;
+import com.android.emailcommon.provider.HostAuth;
import com.android.mail.utils.LogUtils;
+import com.google.common.annotations.VisibleForTesting;
-public class AccountSetupFinal extends AccountSetupActivity implements View.OnClickListener {
- private static final String SAVESTATE_IS_PROCESSING_KEY =
- "com.android.email.AccountSetupFinal.is_processing";
- private static final String SAVESTATE_STATE = "com.android.email.AccountSetupFinal.state";
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
- private static final String ACCOUNT_CREATION_FRAGMENT_TAG = "AccountCreationFragment";
- private static final String ACCOUNT_FINALIZE_FRAGMENT_TAG = "AccountFinalizeFragment";
+public class AccountSetupFinal extends AccountSetupActivity
+ implements View.OnClickListener, AccountFinalizeFragment.Callback,
+ AccountSetupNoteDialogFragment.Callback, AccountCreationFragment.Callback,
+ AccountCheckSettingsFragment.Callback, SecurityRequiredDialogFragment.Callback,
+ CheckSettingsErrorDialogFragment.Callback, CheckSettingsProgressDialogFragment.Callback,
+ AccountSetupTypeFragment.Callback, AccountSetupNamesFragment.Callback,
+ AccountSetupOptionsFragment.Callback, AccountSetupBasicsFragment.Callback,
+ AccountServerBaseFragment.Callback, AccountSetupCredentialsFragment.Callback {
- private static final String CREATE_ACCOUNT_DIALOG_TAG = "CreateAccountDialog";
+ // STOPSHIP: Set to false before shipping, logs PII
+ private final static boolean ENTER_DEBUG_SCREEN = true;
+
+ /**
+ * Direct access for forcing account creation
+ * For use by continuous automated test system (e.g. in conjunction with monkey tests)
+ *
+ * === Support for automated testing ==
+ * This activity can also be launched directly via INTENT_CREATE_ACCOUNT. This is intended
+ * only for use by continuous test systems, and is currently only available when
+ * {@link ActivityManager#isRunningInTestHarness()} is set. To use this mode, you must
+ * construct an intent which contains all necessary information to create the account. No
+ * connection checking is done, so the account may or may not actually work. Here is a sample
+ * command, for a gmail account "test_account" with a password of "test_password".
+ *
+ * $ adb shell am start -a com.android.email.CREATE_ACCOUNT \
+ * -e EMAIL test_account@gmail.com \
+ * -e USER "Test Account Name" \
+ * -e INCOMING imap+ssl+://test_account:test_password@imap.gmail.com \
+ * -e OUTGOING smtp+ssl+://test_account:test_password@smtp.gmail.com
+ *
+ * Note: For accounts that require the full email address in the login, encode the @ as %40.
+ * Note: Exchange accounts that require device security policies cannot be created
+ * automatically.
+ */
+ private static String INTENT_CREATE_ACCOUNT;
+ private static final String EXTRA_FLOW_MODE = "FLOW_MODE";
+ private static final String EXTRA_FLOW_ACCOUNT_TYPE = "FLOW_ACCOUNT_TYPE";
+ private static final String EXTRA_CREATE_ACCOUNT_EMAIL = "EMAIL";
+ private static final String EXTRA_CREATE_ACCOUNT_USER = "USER";
+ private static final String EXTRA_CREATE_ACCOUNT_INCOMING = "INCOMING";
+ private static final String EXTRA_CREATE_ACCOUNT_OUTGOING = "OUTGOING";
+ private static final Boolean DEBUG_ALLOW_NON_TEST_HARNESS_CREATION = false;
+
+ protected static final String ACTION_JUMP_TO_INCOMING = "jumpToIncoming";
+ protected static final String ACTION_JUMP_TO_OUTGOING = "jumpToOutgoing";
+ protected static final String ACTION_JUMP_TO_OPTIONS = "jumpToOptions";
+
+ private static final String SAVESTATE_KEY_IS_PROCESSING = "AccountSetupFinal.is_processing";
+ private static final String SAVESTATE_KEY_STATE = "AccountSetupFinal.state";
+ private static final String SAVESTATE_KEY_PROVIDER = "AccountSetupFinal.provider";
+ private static final String SAVESTATE_KEY_AUTHENTICATOR_RESPONSE = "AccountSetupFinal.authResp";
+ private static final String SAVESTATE_KEY_REPORT_AUTHENTICATOR_ERROR =
+ "AccountSetupFinal.authErr";
+ private static final String SAVESTATE_KEY_IS_PRE_CONFIGURED = "AccountSetupFinal.preconfig";
+ private static final String SAVESTATE_KEY_SKIP_AUTO_DISCOVER = "AccountSetupFinal.noAuto";
private static final String CONTENT_FRAGMENT_TAG = "AccountSetupContentFragment";
- private static final int STATE_OPTIONS = 0;
- private static final int STATE_NAMES = 1;
- private int mState = STATE_OPTIONS;
+ // Collecting initial email and password
+ private static final int STATE_BASICS = 0;
+ // Collect initial password or oauth token
+ private static final int STATE_CREDENTIALS = 1;
+ // Show the user some interstitial message after credential entry
+ private static final int STATE_CREDENTIALS_POST = 2;
+ // User chose the "Manual Setup" button
+ private static final int STATE_DIVERT_TO_MANUAL = 3;
+ // Account is a pre-configured account, run the checker
+ private static final int STATE_CHECKING_PRECONFIGURED = 4;
+ // Account is not pre-configured, query user for account type
+ private static final int STATE_TYPE = 5;
+ // Auto-discovering exchange account info, possibly other protocols later
+ private static final int STATE_AUTO_DISCOVER = 6;
+ // User is entering incoming settings
+ private static final int STATE_MANUAL_INCOMING = 7;
+ // We're checking incoming settings
+ private static final int STATE_CHECKING_INCOMING = 8;
+ // User is entering outgoing settings
+ private static final int STATE_MANUAL_OUTGOING = 9;
+ // We're checking outgoing settings
+ private static final int STATE_CHECKING_OUTGOING = 10;
+ // User is entering sync options
+ private static final int STATE_OPTIONS = 11;
+ // We're creating the account
+ private static final int STATE_CREATING = 12;
+ // User is entering account name and real name
+ private static final int STATE_NAMES = 13;
+ // we're finalizing the account
+ private static final int STATE_FINALIZE = 14;
+
+ private int mState = STATE_BASICS;
private boolean mIsProcessing = false;
private boolean mForceCreate = false;
+ private boolean mReportAccountAuthenticatorError;
+ private AccountAuthenticatorResponse mAccountAuthenticatorResponse;
+ // True if this provider is found in our providers.xml, set after Basics
+ private boolean mIsPreConfiguredProvider = false;
+ // True if the user selected manual setup
+ private boolean mSkipAutoDiscover = false;
+ // True if validating the pre-configured provider failed and we want manual setup
+ private boolean mPreConfiguredFailed = false;
+
+ private VendorPolicyLoader.Provider mProvider;
+
+ // UI elements
+ @VisibleForTesting
+ protected ImageButton mNextButton;
+
+ private static final int OWNER_NAME_LOADER_ID = 0;
+ private String mOwnerName;
+
+ private static final int EXISTING_ACCOUNTS_LOADER_ID = 1;
+ private Map<String, String> mExistingAccountsMap;
+
+ public static Intent actionNewAccountIntent(final Context context) {
+ final Intent i = new Intent(context, AccountSetupFinal.class);
+ i.putExtra(EXTRA_FLOW_MODE, SetupDataFragment.FLOW_MODE_NORMAL);
+ return i;
+ }
- private ImageButton mNextButton;
+ public static Intent actionNewAccountWithResultIntent(final Context context) {
+ final Intent i = new ForwardingIntent(context, AccountSetupFinal.class);
+ i.putExtra(EXTRA_FLOW_MODE, SetupDataFragment.FLOW_MODE_NO_ACCOUNTS);
+ return i;
+ }
- public static void actionFinal(Activity fromActivity, SetupDataFragment setupData) {
- final Intent intent = new ForwardingIntent(fromActivity, AccountSetupFinal.class);
- intent.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, setupData);
- fromActivity.startActivity(intent);
+ public static Intent actionGetCreateAccountIntent(final Context context,
+ final String accountManagerType) {
+ final Intent i = new Intent(context, AccountSetupFinal.class);
+ i.putExtra(EXTRA_FLOW_MODE, SetupDataFragment.FLOW_MODE_ACCOUNT_MANAGER);
+ i.putExtra(EXTRA_FLOW_ACCOUNT_TYPE, accountManagerType);
+ return i;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ final Intent intent = getIntent();
+ final String action = intent.getAction();
+
+ if (INTENT_CREATE_ACCOUNT == null) {
+ INTENT_CREATE_ACCOUNT = getString(R.string.intent_create_account);
+ }
+
setContentView(R.layout.account_setup);
if (savedInstanceState != null) {
- mIsProcessing = savedInstanceState.getBoolean(SAVESTATE_IS_PROCESSING_KEY, false);
- mState = savedInstanceState.getInt(SAVESTATE_STATE, STATE_OPTIONS);
+ mIsProcessing = savedInstanceState.getBoolean(SAVESTATE_KEY_IS_PROCESSING, false);
+ mState = savedInstanceState.getInt(SAVESTATE_KEY_STATE, STATE_OPTIONS);
+ mProvider = (VendorPolicyLoader.Provider)
+ savedInstanceState.getSerializable(SAVESTATE_KEY_PROVIDER);
+ mAccountAuthenticatorResponse =
+ savedInstanceState.getParcelable(SAVESTATE_KEY_AUTHENTICATOR_RESPONSE);
+ mReportAccountAuthenticatorError =
+ savedInstanceState.getBoolean(SAVESTATE_KEY_REPORT_AUTHENTICATOR_ERROR);
+ mIsPreConfiguredProvider =
+ savedInstanceState.getBoolean(SAVESTATE_KEY_IS_PRE_CONFIGURED);
+ mSkipAutoDiscover = savedInstanceState.getBoolean(SAVESTATE_KEY_SKIP_AUTO_DISCOVER);
} else {
// If we're not restoring from a previous state, we want to configure the initial screen
- mState = STATE_OPTIONS;
+
+ // Set aside incoming AccountAuthenticatorResponse, if there was any
+ mAccountAuthenticatorResponse = getIntent()
+ .getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
+ if (mAccountAuthenticatorResponse != null) {
+ // When this Activity is called as part of account authentification flow,
+ // we are responsible for eventually reporting the result (success or failure) to
+ // the account manager. Most exit paths represent an failed or abandoned setup,
+ // so the default is to report the error. Success will be reported by the code in
+ // AccountSetupOptions that commits the finally created account.
+ mReportAccountAuthenticatorError = true;
+ }
+
+ // Initialize the SetupDataFragment
+ if (INTENT_CREATE_ACCOUNT.equals(action)) {
+ mSetupData.setFlowMode(SetupDataFragment.FLOW_MODE_FORCE_CREATE);
+ } else {
+ final int intentFlowMode = intent.getIntExtra(EXTRA_FLOW_MODE,
+ SetupDataFragment.FLOW_MODE_UNSPECIFIED);
+ // TODO: do something with this
+ final String flowAccountType = intent.getStringExtra(EXTRA_FLOW_ACCOUNT_TYPE);
+ mSetupData.setFlowMode(intentFlowMode);
+ }
+
+ mState = STATE_BASICS;
+ // Support unit testing individual screens
+ if (TextUtils.equals(ACTION_JUMP_TO_INCOMING, action)) {
+ mState = STATE_MANUAL_INCOMING;
+ } else if (TextUtils.equals(ACTION_JUMP_TO_OUTGOING, action)) {
+ mState = STATE_MANUAL_OUTGOING;
+ } else if (TextUtils.equals(ACTION_JUMP_TO_OPTIONS, action)) {
+ mState = STATE_OPTIONS;
+ }
updateHeadline();
updateContentFragment(false /* addToBackstack */);
}
@@ -96,28 +257,143 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
if (!mIsProcessing
&& mSetupData.getFlowMode() == SetupDataFragment.FLOW_MODE_FORCE_CREATE) {
- // If we are just visiting here to fill in details, exit immediately
getFragmentManager().executePendingTransactions();
+
+ if (!DEBUG_ALLOW_NON_TEST_HARNESS_CREATION &&
+ !ActivityManager.isRunningInTestHarness()) {
+ LogUtils.e(LogUtils.TAG,
+ "ERROR: Force account create only allowed while in test harness");
+ finish();
+ return;
+ }
+
mForceCreate = true;
}
+
+ // Launch a loader to look up the owner name. It should be ready well in advance of
+ // the time the user clicks next or manual.
+ getLoaderManager().initLoader(OWNER_NAME_LOADER_ID, null,
+ new LoaderManager.LoaderCallbacks<Cursor>() {
+ @Override
+ public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
+ return new CursorLoader(AccountSetupFinal.this,
+ ContactsContract.Profile.CONTENT_URI,
+ new String[] {ContactsContract.Profile.DISPLAY_NAME_PRIMARY},
+ null, null, null);
+ }
+
+ @Override
+ public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) {
+ if (data != null && data.moveToFirst()) {
+ mOwnerName = data.getString(data.getColumnIndex(
+ ContactsContract.Profile.DISPLAY_NAME_PRIMARY));
+ }
+ }
+
+ @Override
+ public void onLoaderReset(final Loader<Cursor> loader) {}
+ });
+
+ // Launch a loader to cache some info about existing accounts so we can dupe-check against
+ // them.
+ getLoaderManager().initLoader(EXISTING_ACCOUNTS_LOADER_ID, null,
+ new LoaderManager.LoaderCallbacks<Cursor> () {
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ return new CursorLoader(AccountSetupFinal.this, Account.CONTENT_URI,
+ new String[] {Account.EMAIL_ADDRESS, Account.DISPLAY_NAME},
+ null, null, null);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ if (data == null) {
+ mExistingAccountsMap = null;
+ return;
+ }
+
+ mExistingAccountsMap = new HashMap<String, String>();
+
+ final int emailColumnIndex = data.getColumnIndex(Account.EMAIL_ADDRESS);
+ final int displayNameColumnIndex =
+ data.getColumnIndex(Account.DISPLAY_NAME);
+
+ while (data.moveToNext()) {
+ final String email = data.getString(emailColumnIndex);
+ final String displayName = data.getString(displayNameColumnIndex);
+ mExistingAccountsMap.put(email,
+ TextUtils.isEmpty(displayName) ? email : displayName);
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mExistingAccountsMap = null;
+ }
+ });
}
@Override
protected void onResume() {
super.onResume();
if (mForceCreate) {
- mForceCreate = false;
- // We need to do this after onCreate so that we can ensure that the fragment is fully
- // created before querying it.
- initiateAccountCreation();
+ try {
+ mForceCreate = false;
+
+ /**
+ * To support continuous testing, we allow the forced creation of accounts.
+ * This works in a manner fairly similar to automatic setup, in which the complete
+ * server Uri's are available, except that we will also skip checking (as if both
+ * checks were true) and all other UI.
+ *
+ * email: The email address for the new account
+ * user: The user name for the new account
+ * incoming: The URI-style string defining the incoming account
+ * outgoing: The URI-style string defining the outgoing account
+ */
+ final Intent intent = getIntent();
+ final String email = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_EMAIL);
+ final String user = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_USER);
+ final String incoming = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_INCOMING);
+ final String outgoing = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_OUTGOING);
+ if (TextUtils.isEmpty(email) || TextUtils.isEmpty(user) ||
+ TextUtils.isEmpty(incoming) || TextUtils.isEmpty(outgoing)) {
+ LogUtils.e(LogUtils.TAG, "ERROR: Force account create requires extras EMAIL, " +
+ "USER, INCOMING, OUTGOING");
+ finish();
+ return;
+ }
+
+ final Account account = mSetupData.getAccount();
+ final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
+ recvAuth.setHostAuthFromString(incoming);
+
+ final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
+ sendAuth.setHostAuthFromString(outgoing);
+
+ populateSetupData(user, email);
+
+ // We need to do this after onCreate so that we can ensure that the fragment is
+ // fully created before querying it.
+ initiateAccountCreation();
+ } catch (URISyntaxException e) {
+ // If we can't set up the URL, don't continue
+ Toast.makeText(this,
+ R.string.account_setup_username_password_toast, Toast.LENGTH_LONG).show();
+ }
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putBoolean(SAVESTATE_IS_PROCESSING_KEY, mIsProcessing);
- outState.putInt(SAVESTATE_STATE, mState);
+ outState.putBoolean(SAVESTATE_KEY_IS_PROCESSING, mIsProcessing);
+ outState.putInt(SAVESTATE_KEY_STATE, mState);
+ outState.putSerializable(SAVESTATE_KEY_PROVIDER, mProvider);
+ outState.putParcelable(SAVESTATE_KEY_AUTHENTICATOR_RESPONSE, mAccountAuthenticatorResponse);
+ outState.putBoolean(SAVESTATE_KEY_REPORT_AUTHENTICATOR_ERROR,
+ mReportAccountAuthenticatorError);
+ outState.putBoolean(SAVESTATE_KEY_IS_PRE_CONFIGURED, mIsPreConfiguredProvider);
}
/**
@@ -126,12 +402,30 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
private void updateHeadline() {
TextView headlineView = UiUtilities.getView(this, R.id.headline);
switch (mState) {
+ case STATE_BASICS:
+ headlineView.setText(R.string.account_setup_basics_headline);
+ break;
+ case STATE_CREDENTIALS:
+ // TODO: real string
+ headlineView.setText(R.string.sign_in_title);
+ break;
+ case STATE_TYPE:
+ headlineView.setText(R.string.account_setup_account_type_headline);
+ break;
+ case STATE_MANUAL_INCOMING:
+ headlineView.setText(R.string.account_setup_incoming_headline);
+ break;
+ case STATE_MANUAL_OUTGOING:
+ headlineView.setText(R.string.account_setup_outgoing_headline);
+ break;
case STATE_OPTIONS:
headlineView.setText(R.string.account_setup_options_headline);
break;
case STATE_NAMES:
headlineView.setText(R.string.account_setup_names_headline);
break;
+ default:
+ throw new IllegalStateException("Incorrect state" + mState);
}
}
@@ -140,17 +434,34 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
* stack, so only call it once per transition.
*/
private void updateContentFragment(boolean addToBackstack) {
- final Fragment f;
+ final AccountSetupFragment f;
switch (mState) {
+ case STATE_BASICS:
+ f = AccountSetupBasicsFragment.newInstance();
+ break;
+ case STATE_CREDENTIALS:
+ f = AccountSetupCredentialsFragment.newInstance(mSetupData.getEmail(),
+ mSetupData.getAccount().getOrCreateHostAuthRecv(this).mProtocol);
+ break;
+ case STATE_TYPE:
+ f = AccountSetupTypeFragment.newInstance();
+ break;
+ case STATE_MANUAL_INCOMING:
+ f = AccountSetupIncomingFragment.newInstance(false);
+ break;
+ case STATE_MANUAL_OUTGOING:
+ f = AccountSetupOutgoingFragment.newInstance(false);
+ break;
case STATE_OPTIONS:
- f = new AccountSetupOptionsFragment();
+ f = AccountSetupOptionsFragment.newInstance();
break;
case STATE_NAMES:
- f = new AccountSetupNamesFragment();
+ f = AccountSetupNamesFragment.newInstance();
break;
default:
- throw new IllegalStateException("Unknown state " + mState);
+ throw new IllegalStateException("Incorrect state " + mState);
}
+ f.setState(mState);
final FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.setup_fragment_container, f, CONTENT_FRAGMENT_TAG);
if (addToBackstack) {
@@ -163,8 +474,8 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
* Retrieve the current content fragment
* @return The content fragment or null if it wasn't found for some reason
*/
- private Fragment getContentFragment() {
- return getFragmentManager().findFragmentByTag(CONTENT_FRAGMENT_TAG);
+ private AccountSetupFragment getContentFragment() {
+ return (AccountSetupFragment) getFragmentManager().findFragmentByTag(CONTENT_FRAGMENT_TAG);
}
/**
@@ -175,8 +486,98 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
mIsProcessing = false;
setNextButtonEnabled(true);
+ getFragmentManager().executePendingTransactions();
+
switch (mState) {
+ case STATE_BASICS:
+ onBasicsComplete();
+ mState = STATE_CREDENTIALS;
+ updateHeadline();
+ updateContentFragment(true /* addToBackstack */);
+ break;
+ case STATE_DIVERT_TO_MANUAL:
+ mState = STATE_TYPE;
+ updateHeadline();
+ updateContentFragment(true /* addToBackstack */);
+ break;
+ case STATE_CREDENTIALS:
+ final boolean advance = collectCredentials();
+ if (!advance) {
+ mState = STATE_CREDENTIALS_POST;
+ break;
+ } // else fall through
+ case STATE_CREDENTIALS_POST:
+ if (mIsPreConfiguredProvider) {
+ mState = STATE_CHECKING_PRECONFIGURED;
+ initiateCheckSettingsFragment(SetupDataFragment.CHECK_INCOMING
+ | SetupDataFragment.CHECK_OUTGOING);
+ } else {
+ mState = STATE_TYPE;
+ updateHeadline();
+ updateContentFragment(true /* addToBackstack */);
+ }
+ break;
+ case STATE_CHECKING_PRECONFIGURED:
+ if (mPreConfiguredFailed) {
+ mState = STATE_MANUAL_INCOMING;
+ updateHeadline();
+ updateContentFragment(true /* addToBackstack */);
+ } else {
+ mState = STATE_OPTIONS;
+ updateHeadline();
+ updateContentFragment(true /* addToBackstack */);
+ }
+ break;
+ case STATE_TYPE:
+ // We either got here through "Manual Setup" or because we didn't find the provider
+ // In the former case, we skip auto-discover
+ if (mSkipAutoDiscover) {
+ mState = STATE_MANUAL_INCOMING;
+ updateHeadline();
+ updateContentFragment(true /* addToBackstack */);
+ } else {
+ mState = STATE_AUTO_DISCOVER;
+ initiateCheckSettingsFragment(SetupDataFragment.CHECK_AUTODISCOVER);
+ }
+ break;
+ case STATE_AUTO_DISCOVER:
+ // TODO: figure out if we can skip past manual setup
+ mState = STATE_MANUAL_INCOMING;
+ updateHeadline();
+ updateContentFragment(true);
+ break;
+ case STATE_MANUAL_INCOMING:
+ onIncomingComplete();
+ mState = STATE_CHECKING_INCOMING;
+ initiateCheckSettingsFragment(SetupDataFragment.CHECK_INCOMING);
+ break;
+ case STATE_CHECKING_INCOMING:
+ final EmailServiceUtils.EmailServiceInfo serviceInfo =
+ EmailServiceUtils.getServiceInfo(this,
+ getSetupData().getAccount().getProtocol(this));
+ if (serviceInfo.usesSmtp) {
+ mState = STATE_MANUAL_OUTGOING;
+ } else {
+ mState = STATE_OPTIONS;
+ }
+ updateHeadline();
+ updateContentFragment(true /* addToBackstack */);
+ break;
+ case STATE_MANUAL_OUTGOING:
+ onOutgoingComplete();
+ mState = STATE_CHECKING_OUTGOING;
+ initiateCheckSettingsFragment(SetupDataFragment.CHECK_OUTGOING);
+ break;
+ case STATE_CHECKING_OUTGOING:
+ mState = STATE_OPTIONS;
+ updateHeadline();
+ updateContentFragment(true /* addToBackstack */);
+ break;
case STATE_OPTIONS:
+ mState = STATE_CREATING;
+ initiateAccountCreation();
+ break;
+ case STATE_CREATING:
mState = STATE_NAMES;
updateHeadline();
updateContentFragment(true /* addToBackstack */);
@@ -186,7 +587,13 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
}
break;
case STATE_NAMES:
- finishActivity();
+ initiateAccountFinalize();
+ break;
+ case STATE_FINALIZE:
+ finish();
+ break;
+ default:
+ LogUtils.wtf(LogUtils.TAG, "Unknown state %d", mState);
break;
}
}
@@ -200,24 +607,34 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
return;
}
if (mState == STATE_NAMES) {
- finishActivity();
+ finish();
} else {
super.onBackPressed();
}
+ // After super.onBackPressed() our fragment should be in place, so query the state we
+ // installed it for
+ AccountSetupFragment f = getContentFragment();
+ mState = f.getState();
+ updateHeadline();
}
- private void finishActivity() {
- if (mSetupData.getFlowMode() == SetupDataFragment.FLOW_MODE_NO_ACCOUNTS) {
- AccountSetupBasics.actionAccountCreateFinishedWithResult(this);
- } else if (mSetupData.getFlowMode() != SetupDataFragment.FLOW_MODE_NORMAL) {
- AccountSetupBasics.actionAccountCreateFinishedAccountFlow(this);
- } else {
- final Account account = mSetupData.getAccount();
- if (account != null) {
- AccountSetupBasics.actionAccountCreateFinished(this, account);
+ @Override
+ public void setAccount(Account account) {
+ mSetupData.setAccount(account);
+ }
+
+ @Override
+ public void finish() {
+ // If the account manager initiated the creation, and success was not reported,
+ // then we assume that we're giving up (for any reason) - report failure.
+ if (mReportAccountAuthenticatorError) {
+ if (mAccountAuthenticatorResponse != null) {
+ mAccountAuthenticatorResponse
+ .onError(AccountManager.ERROR_CODE_CANCELED, "canceled");
+ mAccountAuthenticatorResponse = null;
}
}
- finish();
+ super.finish();
}
/**
@@ -227,17 +644,9 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
public void onClick(View view) {
switch (view.getId()) {
case R.id.next:
- // Debounce touches
+ // Some states are handled without UI, block double-presses here
if (!mIsProcessing) {
- switch (mState) {
- case STATE_OPTIONS:
- initiateAccountCreation();
- break;
- case STATE_NAMES:
- initiateAccountFinalize();
- break;
- }
- setNextButtonEnabled(false);
+ proceed();
}
break;
case R.id.previous:
@@ -250,6 +659,266 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
mNextButton.setEnabled(enabled);
}
+ @Override
+ public void onBasicsManualSetupButton() {
+ final AccountSetupBasicsFragment basicsFragment =
+ (AccountSetupBasicsFragment) getContentFragment();
+ final String email = basicsFragment.getEmail();
+ mSetupData.setEmail(email);
+
+ // Alternate entry to the debug options screen (for devices without a physical keyboard:
+ // Username: d@d.d
+ if (ENTER_DEBUG_SCREEN) {
+ if ("d@d.d".equals(mSetupData.getEmail())) {
+ AccountSettings.actionSettingsWithDebug(this);
+ return;
+ }
+ }
+ mState = STATE_DIVERT_TO_MANUAL;
+ mSkipAutoDiscover = true;
+ proceed();
+ }
+
+ private void onBasicsComplete() {
+ final AccountSetupBasicsFragment f = (AccountSetupBasicsFragment) getContentFragment();
+ final String email = f.getEmail();
+ mSetupData.setEmail(email);
+ }
+
+ @Override
+ public void onCredentialsComplete(Bundle results) {
+ proceed();
+ }
+
+ /**
+ *
+ * @return true to proceed, false to remain on the current screen
+ */
+ private boolean collectCredentials() {
+ final AccountSetupCredentialsFragment f = (AccountSetupCredentialsFragment)
+ getContentFragment();
+ final Bundle results = f.getCredentialResults();
+ mSetupData.setCredentialResults(results);
+ final String email = mSetupData.getEmail();
+ final String[] emailParts = email.split("@");
+ final String domain = emailParts[1].trim();
+ mProvider = AccountSettingsUtils.findProviderForDomain(this, domain);
+ if (mProvider != null) {
+ mProvider.expandTemplates(email);
+ mIsPreConfiguredProvider = true;
+ if (mProvider.note != null) {
+ final AccountSetupNoteDialogFragment dialogFragment =
+ AccountSetupNoteDialogFragment.newInstance(mProvider.note);
+ dialogFragment.show(getFragmentManager(), AccountSetupNoteDialogFragment.TAG);
+ return false;
+ } else {
+ return finishAutoSetup();
+ }
+ } else {
+ // Can't use auto setup (although EAS accounts may still be able to AutoDiscover)
+ mIsPreConfiguredProvider = false;
+ final String existingAccountName = mExistingAccountsMap.get(email);
+ if (!TextUtils.isEmpty(existingAccountName)) {
+ final DuplicateAccountDialogFragment dialogFragment =
+ DuplicateAccountDialogFragment.newInstance(existingAccountName);
+ dialogFragment.show(getFragmentManager(), DuplicateAccountDialogFragment.TAG);
+ return false;
+ } else {
+ populateSetupData(mOwnerName, email);
+ mSkipAutoDiscover = false;
+ return true;
+ }
+ }
+ }
+
+ @Override
+ public void onNoteDialogComplete() {
+ finishAutoSetup();
+ }
+
+ /**
+ * Finish the auto setup process, in some cases after showing a warning dialog.
+ * Happens after onBasicsComplete
+ * @return true to proceed, false to remain on the current screen
+ */
+ private boolean finishAutoSetup() {
+ final String email = mSetupData.getEmail();
+
+ try {
+ mProvider.expandTemplates(email);
+
+ final Account account = mSetupData.getAccount();
+ final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
+ recvAuth.setHostAuthFromString(mProvider.incomingUri);
+ recvAuth.setUserName(mProvider.incomingUsername);
+ AccountSetupCredentialsFragment.populateHostAuthWithResults(this, recvAuth,
+ mSetupData.getCredentialResults());
+
+ final EmailServiceUtils.EmailServiceInfo info = EmailServiceUtils.getServiceInfo(this,
+ recvAuth.mProtocol);
+ recvAuth.mPort =
+ ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) ? info.portSsl : info.port;
+
+ final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
+ sendAuth.setHostAuthFromString(mProvider.outgoingUri);
+ sendAuth.setUserName(mProvider.outgoingUsername);
+ AccountSetupCredentialsFragment.populateHostAuthWithResults(this, sendAuth,
+ mSetupData.getCredentialResults());
+
+ // Populate the setup data, assuming that the duplicate account check will succeed
+ populateSetupData(mOwnerName, email);
+
+ final String duplicateAccountName =
+ mExistingAccountsMap != null ? mExistingAccountsMap.get(email) : null;
+ if (duplicateAccountName != null) {
+ final DuplicateAccountDialogFragment dialogFragment =
+ DuplicateAccountDialogFragment.newInstance(duplicateAccountName);
+ dialogFragment.show(getFragmentManager(), DuplicateAccountDialogFragment.TAG);
+ return false;
+ }
+ } catch (URISyntaxException e) {
+ mSkipAutoDiscover = false;
+ mPreConfiguredFailed = true;
+ }
+ return true;
+ }
+
+
+ /**
+ * Helper method to fill in some per-protocol defaults
+ * @param account Account object to fill in
+ */
+ public void setDefaultsForProtocol(Account account) {
+ final String protocol = account.getOrCreateHostAuthRecv(this).mProtocol;
+ if (protocol == null) return;
+ final EmailServiceUtils.EmailServiceInfo info =
+ EmailServiceUtils.getServiceInfo(this, protocol);
+ account.mSyncInterval = info.defaultSyncInterval;
+ account.mSyncLookback = info.defaultLookback;
+ if (info.offerLocalDeletes) {
+ account.setDeletePolicy(info.defaultLocalDeletes);
+ }
+ }
+
+ /**
+ * Populate SetupData's account with complete setup info, assumes that the receive auth is
+ * created and its protocol is set
+ */
+ private void populateSetupData(String senderName, String senderEmail) {
+ final Account account = mSetupData.getAccount();
+ account.setSenderName(senderName);
+ account.setEmailAddress(senderEmail);
+ account.setDisplayName(senderEmail);
+ setDefaultsForProtocol(account);
+ }
+
+ private void onIncomingComplete() {
+ AccountSetupIncomingFragment f = (AccountSetupIncomingFragment) getContentFragment();
+ f.collectUserInput();
+ }
+
+ private void onOutgoingComplete() {
+ AccountSetupOutgoingFragment f = (AccountSetupOutgoingFragment) getContentFragment();
+ f.collectUserInput();
+ }
+
+ // This callback method is only applicable to using Incoming/Outgoing fragments in settings mode
+ @Override
+ public void onAccountServerUIComplete(int checkMode) {}
+
+ // This callback method is only applicable to using Incoming/Outgoing fragments in settings mode
+ @Override
+ public void onAccountServerSaveComplete() {}
+
+ private void initiateCheckSettingsFragment(int checkMode) {
+ final Fragment f = AccountCheckSettingsFragment.newInstance(checkMode);
+ final Fragment d = CheckSettingsProgressDialogFragment.newInstance(checkMode);
+ getFragmentManager().beginTransaction()
+ .add(f, AccountCheckSettingsFragment.TAG)
+ .add(d, CheckSettingsProgressDialogFragment.TAG)
+ .commit();
+ }
+
+ @Override
+ public void onCheckSettingsProgressDialogCancel() {
+ dismissCheckSettingsFragment();
+ }
+
+ private void dismissCheckSettingsFragment() {
+ final Fragment f = getFragmentManager().findFragmentByTag(AccountCheckSettingsFragment.TAG);
+ final Fragment d =
+ getFragmentManager().findFragmentByTag(CheckSettingsProgressDialogFragment.TAG);
+ getFragmentManager().beginTransaction()
+ .remove(f)
+ .remove(d)
+ .commit();
+ }
+
+ @Override
+ public void onCheckSettingsError(int reason, String message) {
+ dismissCheckSettingsFragment();
+ final DialogFragment f =
+ CheckSettingsErrorDialogFragment.newInstance(reason, message);
+ f.show(getFragmentManager(), CheckSettingsErrorDialogFragment.TAG);
+ }
+
+ @Override
+ public void onCheckSettingsErrorDialogEditCertificate() {
+ if (mState == STATE_CHECKING_PRECONFIGURED) {
+ mPreConfiguredFailed = true;
+ proceed();
+ }
+ final AccountSetupIncomingFragment f = (AccountSetupIncomingFragment) getContentFragment();
+ f.onCertificateRequested();
+ }
+
+ @Override
+ public void onCheckSettingsErrorDialogEditSettings() {
+ // If we're checking pre-configured, set a flag that we failed and navigate fowards to
+ // incoming settings
+ if (mState == STATE_CHECKING_PRECONFIGURED || mState == STATE_AUTO_DISCOVER) {
+ mPreConfiguredFailed = true;
+ proceed();
+ }
+ }
+
+ @Override
+ public void onCheckSettingsComplete() {
+ dismissCheckSettingsFragment();
+ proceed();
+ }
+
+ @Override
+ public void onCheckSettingsAutoDiscoverComplete(int result) {
+ dismissCheckSettingsFragment();
+ proceed();
+ }
+
+ @Override
+ public void onCheckSettingsSecurityRequired(String hostName) {
+ dismissCheckSettingsFragment();
+ final DialogFragment f = SecurityRequiredDialogFragment.newInstance(hostName);
+ f.show(getFragmentManager(), SecurityRequiredDialogFragment.TAG);
+ }
+
+ @Override
+ public void onSecurityRequiredDialogResult(boolean ok) {
+ if (ok) {
+ proceed();
+ }
+ }
+
+ @Override
+ public void onChooseProtocol(String protocol) {
+ final Account account = mSetupData.getAccount();
+ final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
+ recvAuth.setConnection(protocol, recvAuth.mAddress, recvAuth.mPort, recvAuth.mFlags);
+
+ recvAuth.mLogin = recvAuth.mLogin + "@" + recvAuth.mAddress;
+ setDefaultsForProtocol(account);
+ proceed();
+ }
+
/**
* Ths is called when the user clicks the "done" button.
* It collects the data from the UI, updates the setup account record, and launches a fragment
@@ -257,6 +926,7 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
*/
private void initiateAccountCreation() {
mIsProcessing = true;
+ setNextButtonEnabled(false);
final Account account = mSetupData.getAccount();
if (account.mHostAuthRecv == null) {
@@ -302,7 +972,7 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
final Fragment f = AccountCreationFragment.newInstance(account, syncEmail, syncCalendar,
syncContacts, enableNotifications);
final FragmentTransaction ft = getFragmentManager().beginTransaction();
- ft.add(f, ACCOUNT_CREATION_FRAGMENT_TAG);
+ ft.add(f, AccountCreationFragment.TAG);
ft.commit();
showCreateAccountDialog();
@@ -312,15 +982,20 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
* Called by the account creation fragment after it has completed.
* We do a small amount of work here before moving on to the next state.
*/
- public void proceedFromAccountCreationFragment() {
+ public void onAccountCreationFragmentComplete() {
destroyAccountCreationFragment();
// If the account manager initiated the creation, and success was not reported,
// then we assume that we're giving up (for any reason) - report failure.
- final AccountAuthenticatorResponse authenticatorResponse =
- mSetupData.getAccountAuthenticatorResponse();
- if (authenticatorResponse != null) {
- authenticatorResponse.onError(AccountManager.ERROR_CODE_CANCELED, "canceled");
- mSetupData.setAccountAuthenticatorResponse(null);
+ if (mAccountAuthenticatorResponse != null) {
+ final Account account = getSetupData().getAccount();
+ final EmailServiceUtils.EmailServiceInfo info =
+ EmailServiceUtils.getServiceInfo(this, account.getProtocol(this));
+ final Bundle b = new Bundle(2);
+ b.putString(AccountManager.KEY_ACCOUNT_NAME, account.getEmailAddress());
+ b.putString(AccountManager.KEY_ACCOUNT_TYPE, info.accountType);
+ mAccountAuthenticatorResponse.onResult(b);
+ mAccountAuthenticatorResponse = null;
+ mReportAccountAuthenticatorError = false;
}
proceed();
}
@@ -328,19 +1003,24 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
public void destroyAccountCreationFragment() {
dismissCreateAccountDialog();
- final Fragment f = getFragmentManager().findFragmentByTag(ACCOUNT_CREATION_FRAGMENT_TAG);
+ final Fragment f = getFragmentManager().findFragmentByTag(AccountCreationFragment.TAG);
if (f == null) {
LogUtils.wtf(LogUtils.TAG, "Couldn't find AccountCreationFragment to destroy");
}
- final FragmentTransaction ft = getFragmentManager().beginTransaction();
- ft.remove(f);
- ft.commit();
+ getFragmentManager().beginTransaction()
+ .remove(f)
+ .commit();
}
public static class CreateAccountDialogFragment extends DialogFragment {
+ public static final String TAG = "CreateAccountDialogFragment";
public CreateAccountDialogFragment() {}
+ public static CreateAccountDialogFragment newInstance() {
+ return new CreateAccountDialogFragment();
+ }
+
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
/// Show "Creating account..." dialog
@@ -352,12 +1032,13 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
}
protected void showCreateAccountDialog() {
- new CreateAccountDialogFragment().show(getFragmentManager(), CREATE_ACCOUNT_DIALOG_TAG);
+ CreateAccountDialogFragment.newInstance()
+ .show(getFragmentManager(), CreateAccountDialogFragment.TAG);
}
protected void dismissCreateAccountDialog() {
final DialogFragment f = (DialogFragment)
- getFragmentManager().findFragmentByTag(CREATE_ACCOUNT_DIALOG_TAG);
+ getFragmentManager().findFragmentByTag(CreateAccountDialogFragment.TAG);
if (f != null) {
f.dismiss();
}
@@ -391,12 +1072,16 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
/**
* This is called if MailService.setupAccountManagerAccount() fails for some reason
*/
- protected void showCreateAccountErrorDialog() {
+ public void showCreateAccountErrorDialog() {
new CreateAccountErrorDialogFragment().show(getFragmentManager(), null);
}
+ /**
+ * Collect the data from AccountSetupNames and finish up account creation
+ */
private void initiateAccountFinalize() {
mIsProcessing = true;
+ setNextButtonEnabled(false);
AccountSetupNamesFragment fragment = (AccountSetupNamesFragment) getContentFragment();
// Update account object from UI
@@ -409,97 +1094,15 @@ public class AccountSetupFinal extends AccountSetupActivity implements View.OnCl
final Fragment f = AccountFinalizeFragment.newInstance(account);
final FragmentTransaction ft = getFragmentManager().beginTransaction();
- ft.add(f, ACCOUNT_FINALIZE_FRAGMENT_TAG);
+ ft.add(f, AccountFinalizeFragment.TAG);
ft.commit();
}
-
- public static class AccountFinalizeFragment extends Fragment {
- private static final String ACCOUNT_TAG = "account";
-
- private static final int FINAL_ACCOUNT_TASK_LOADER_ID = 0;
-
- private Context mAppContext;
-
- public AccountFinalizeFragment() {}
-
- public static AccountFinalizeFragment newInstance(Account account) {
- final AccountFinalizeFragment f = new AccountFinalizeFragment();
- final Bundle args = new Bundle(1);
- args.putParcelable(ACCOUNT_TAG, account);
- f.setArguments(args);
- return f;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mAppContext = getActivity().getApplicationContext();
-
- setRetainInstance(true);
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- getLoaderManager().initLoader(FINAL_ACCOUNT_TASK_LOADER_ID, getArguments(),
- new LoaderManager.LoaderCallbacks<Boolean>() {
- @Override
- public Loader<Boolean> onCreateLoader(int id, Bundle args) {
- final Account accountArg = args.getParcelable(ACCOUNT_TAG);
- return new FinalSetupTaskLoader(mAppContext, accountArg);
- }
-
- @Override
- public void onLoadFinished(Loader<Boolean> loader, Boolean success) {
- if (success && isResumed()) {
- AccountSetupFinal activity = (AccountSetupFinal) getActivity();
- activity.finishActivity();
- }
- }
-
- @Override
- public void onLoaderReset(Loader<Boolean> loader) {
- }
- });
- }
-
- /**
- * Final account setup work is handled in this Loader:
- * Commit final values to provider
- * Trigger account backup
- */
- private static class FinalSetupTaskLoader extends MailAsyncTaskLoader<Boolean> {
-
- private final Account mAccount;
-
- public FinalSetupTaskLoader(Context context, Account account) {
- super(context);
- mAccount = account;
- }
-
- Account getAccount() {
- return mAccount;
- }
-
- @Override
- public Boolean loadInBackground() {
- // Update the account in the database
- final ContentValues cv = new ContentValues();
- cv.put(EmailContent.AccountColumns.DISPLAY_NAME, mAccount.getDisplayName());
- cv.put(EmailContent.AccountColumns.SENDER_NAME, mAccount.getSenderName());
- mAccount.update(getContext(), cv);
-
- // Update the backup (side copy) of the accounts
- AccountBackupRestore.backup(getContext());
-
- return true;
- }
-
- @Override
- protected void onDiscardResult(Boolean result) {}
- }
+ /**
+ * Called when the AccountFinalizeFragment has finished its tasks
+ */
+ @Override
+ public void onAccountFinalizeFragmentComplete() {
+ finish();
}
}
diff --git a/src/com/android/email/activity/setup/AccountSetupFragment.java b/src/com/android/email/activity/setup/AccountSetupFragment.java
new file mode 100644
index 000000000..f38e909a1
--- /dev/null
+++ b/src/com/android/email/activity/setup/AccountSetupFragment.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.email.activity.setup;
+
+import android.app.Fragment;
+
+/**
+ * Superclass for setup UI fragments.
+ * Currently holds a super-interface for the callbacks, as well as the state it was launched for so
+ * we can unwind things correctly when the user navigates the back stack.
+ */
+public class AccountSetupFragment extends Fragment {
+ private int mState;
+
+ public interface Callback {
+ void setNextButtonEnabled(boolean enabled);
+ }
+
+ public void setState(int state) {
+ mState = state;
+ }
+
+ public int getState() {
+ return mState;
+ }
+}
diff --git a/src/com/android/email/activity/setup/AccountSetupIncoming.java b/src/com/android/email/activity/setup/AccountSetupIncoming.java
deleted file mode 100644
index 3973f779c..000000000
--- a/src/com/android/email/activity/setup/AccountSetupIncoming.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.email.activity.setup;
-
-import android.app.Activity;
-import android.app.FragmentTransaction;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.ImageButton;
-
-import com.android.email.R;
-import com.android.email.activity.UiUtilities;
-import com.android.email.service.EmailServiceUtils;
-import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
-import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.HostAuth;
-
-/**
- * Provides setup flow for IMAP/POP accounts.
- *
- * Uses AccountSetupIncomingFragment for primary UI. Uses AccountCheckSettingsFragment to validate
- * the settings as entered. If the account is OK, proceeds to AccountSetupOutgoing.
- */
-public class AccountSetupIncoming extends AccountSetupActivity
- implements AccountSetupIncomingFragment.Callback, OnClickListener {
-
- /* package */ AccountServerBaseFragment mFragment;
- private ImageButton mNextButton;
- /* package */ boolean mNextButtonEnabled;
- private boolean mStartedAutoDiscovery;
- private EmailServiceInfo mServiceInfo;
-
- // Keys for savedInstanceState
- private final static String STATE_STARTED_AUTODISCOVERY =
- "AccountSetupExchange.StartedAutoDiscovery";
-
- // Extras for AccountSetupIncoming intent
-
- public static void actionIncomingSettings(Activity fromActivity, SetupDataFragment setupData) {
- final Intent intent = new Intent(fromActivity, AccountSetupIncoming.class);
- // Add the additional information to the intent, in case the Email process is killed.
- intent.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, setupData);
- fromActivity.startActivity(intent);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- final HostAuth hostAuth = mSetupData.getAccount().mHostAuthRecv;
- mServiceInfo = EmailServiceUtils.getServiceInfo(this, hostAuth.mProtocol);
-
- setContentView(R.layout.account_setup_incoming);
- mFragment = (AccountServerBaseFragment)
- getFragmentManager().findFragmentById(R.id.setup_fragment);
-
- // Configure fragment
- mFragment.setCallback(this);
-
- mNextButton = UiUtilities.getView(this, R.id.next);
- mNextButton.setOnClickListener(this);
- UiUtilities.getView(this, R.id.previous).setOnClickListener(this);
-
- // One-shot to launch autodiscovery at the entry to this activity (but not if it restarts)
- if (mServiceInfo.usesAutodiscover) {
- mStartedAutoDiscovery = false;
- if (savedInstanceState != null) {
- mStartedAutoDiscovery = savedInstanceState.getBoolean(STATE_STARTED_AUTODISCOVERY);
- }
- if (!mStartedAutoDiscovery) {
- startAutoDiscover();
- }
- }
-
- // If we've got a default prefix for this protocol, use it
- final String prefix = mServiceInfo.inferPrefix;
- if (prefix != null && !hostAuth.mAddress.startsWith(prefix + ".")) {
- hostAuth.mAddress = prefix + "." + hostAuth.mAddress;
- }
- }
-
- /**
- * Implements View.OnClickListener
- */
- @Override
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.next:
- mFragment.onNext();
- break;
- case R.id.previous:
- onBackPressed();
- break;
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(STATE_STARTED_AUTODISCOVERY, mStartedAutoDiscovery);
- }
-
- /**
- * If the conditions are right, launch the autodiscover fragment. If it succeeds (even
- * partially) it will prefill the setup fields and we can proceed as if the user entered them.
- *
- * Conditions for skipping:
- * Editing existing account
- * AutoDiscover blocked (used for unit testing only)
- * Username or password not entered yet
- */
- private void startAutoDiscover() {
- // Note that we've started autodiscovery - even if we decide not to do it,
- // this prevents repeating.
- mStartedAutoDiscovery = true;
-
- if (!mSetupData.isAllowAutodiscover()) {
- return;
- }
-
- final Account account = mSetupData.getAccount();
- // If we've got a username and password and we're NOT editing, try autodiscover
- final String username = account.mHostAuthRecv.mLogin;
- final String password = account.mHostAuthRecv.mPassword;
- if (username != null && password != null) {
- onProceedNext(SetupDataFragment.CHECK_AUTODISCOVER, mFragment);
- }
- }
-
- public void onAutoDiscoverComplete(int result, SetupDataFragment setupData) {
- // If authentication failed, exit immediately (to re-enter credentials)
- mSetupData = setupData;
- if (result == AccountCheckSettingsFragment.AUTODISCOVER_AUTHENTICATION) {
- finish();
- return;
- }
-
- // If data was returned, proceed to next screen
- if (result == AccountCheckSettingsFragment.AUTODISCOVER_OK) {
- mFragment.onNext();
- }
- // Otherwise, proceed into this activity for manual setup
- }
-
- /**
- * Implements AccountServerBaseFragment.Callback
- *
- * Launches the account checker. Positive results are reported to onCheckSettingsOk().
- */
- @Override
- public void onProceedNext(int checkMode, AccountServerBaseFragment target) {
- AccountCheckSettingsFragment checkerFragment =
- AccountCheckSettingsFragment.newInstance(checkMode, target);
- FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.add(checkerFragment, AccountCheckSettingsFragment.TAG);
- transaction.addToBackStack("back");
- transaction.commit();
- }
-
- /**
- * Implements AccountServerBaseFragment.Callback
- */
- @Override
- public void onEnableProceedButtons(boolean enable) {
- mNextButtonEnabled = enable;
- mNextButton.setEnabled(enable);
- }
-
- /**
- * Implements AccountServerBaseFragment.Callback
- *
- * If the checked settings are OK, proceed to outgoing settings screen
- */
- @Override
- public void onCheckSettingsComplete(int result, SetupDataFragment setupData) {
- mSetupData = setupData;
- if (result == AccountCheckSettingsFragment.CHECK_SETTINGS_OK) {
- if (mServiceInfo.usesSmtp) {
- AccountSetupOutgoing.actionOutgoingSettings(this, mSetupData);
- } else {
- AccountSetupFinal.actionFinal(this, mSetupData);
- finish();
- }
- }
- }
-}
diff --git a/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java b/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java
index 7f6d796ec..637bec376 100644
--- a/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java
+++ b/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java
@@ -19,12 +19,12 @@ package com.android.email.activity.setup;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.content.Loader;
import android.net.Uri;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
-import android.text.format.DateUtils;
import android.text.method.DigitsKeyListener;
import android.view.LayoutInflater;
import android.view.View;
@@ -44,14 +44,13 @@ import com.android.email.service.EmailServiceUtils;
import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
import com.android.email.view.CertificateSelector;
import com.android.email.view.CertificateSelector.HostCallback;
-import com.android.email2.ui.MailActivityEmail;
import com.android.emailcommon.Device;
-import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.Credential;
import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.utility.CertificateRequestor;
import com.android.emailcommon.utility.Utility;
+import com.android.mail.ui.MailAsyncTaskLoader;
import com.android.mail.utils.LogUtils;
import java.io.IOException;
@@ -91,11 +90,16 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
private TextWatcher mValidationTextWatcher;
// Support for lifecycle
- private boolean mStarted;
private boolean mLoaded;
private String mCacheLoginCredential;
private EmailServiceInfo mServiceInfo;
+ public static AccountSetupIncomingFragment newInstance(boolean settingsMode) {
+ final AccountSetupIncomingFragment f = new AccountSetupIncomingFragment();
+ f.setArguments(getArgs(settingsMode));
+ return f;
+ }
+
// Public no-args constructor needed for fragment re-instantiation
public AccountSetupIncomingFragment() {}
@@ -105,9 +109,6 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
*/
@Override
public void onCreate(Bundle savedInstanceState) {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onCreate");
- }
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
@@ -119,9 +120,6 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onCreateView");
- }
final int layoutId = mSettingsMode
? R.layout.account_settings_incoming_fragment
: R.layout.account_setup_incoming_fragment;
@@ -190,9 +188,6 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
@Override
public void onActivityCreated(Bundle savedInstanceState) {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onActivityCreated");
- }
super.onActivityCreated(savedInstanceState);
mClientCertificateSelector.setHostCallback(this);
@@ -201,8 +196,21 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
(SetupDataFragment.SetupDataContainer) context;
mSetupData = container.getSetupData();
final Account account = mSetupData.getAccount();
- final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
- mServiceInfo = EmailServiceUtils.getServiceInfo(mContext, recvAuth.mProtocol);
+ final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mAppContext);
+
+ // Pre-fill info as appropriate
+ if (!mSetupData.isIncomingCredLoaded()) {
+ recvAuth.mLogin = mSetupData.getEmail();
+ AccountSetupCredentialsFragment.populateHostAuthWithResults(context, recvAuth,
+ mSetupData.getCredentialResults());
+ final String[] emailParts = mSetupData.getEmail().split("@");
+ final String domain = emailParts[1];
+ recvAuth.setConnection(recvAuth.mProtocol, domain, HostAuth.PORT_UNKNOWN,
+ HostAuth.FLAG_NONE);
+ mSetupData.setIncomingCredLoaded(true);
+ }
+
+ mServiceInfo = EmailServiceUtils.getServiceInfo(mAppContext, recvAuth.mProtocol);
if (mServiceInfo.offerLocalDeletes) {
SpinnerOption deletePolicies[] = {
@@ -244,18 +252,7 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
context, android.R.layout.simple_spinner_item, securityTypes);
securityTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSecurityTypeView.setAdapter(securityTypesAdapter);
- }
- /**
- * Called when the Fragment is visible to the user.
- */
- @Override
- public void onStart() {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onStart");
- }
- super.onStart();
- mStarted = true;
configureEditor();
loadSettings();
}
@@ -265,34 +262,11 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
*/
@Override
public void onResume() {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onResume");
- }
super.onResume();
validateFields();
}
@Override
- public void onPause() {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onPause");
- }
- super.onPause();
- }
-
- /**
- * Called when the Fragment is no longer started.
- */
- @Override
- public void onStop() {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onStop");
- }
- super.onStop();
- mStarted = false;
- }
-
- @Override
public void onDestroyView() {
// Make sure we don't get callbacks after the views are supposed to be destroyed
// and also don't hold onto them longer than we need
@@ -323,22 +297,8 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
super.onDestroyView();
}
- /**
- * Called when the fragment is no longer in use.
- */
- @Override
- public void onDestroy() {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onDestroy");
- }
- super.onDestroy();
- }
-
@Override
public void onSaveInstanceState(Bundle outState) {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onSaveInstanceState");
- }
super.onSaveInstanceState(outState);
outState.putString(STATE_KEY_CREDENTIAL, mCacheLoginCredential);
@@ -346,24 +306,12 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
}
/**
- * Activity provides callbacks here. This also triggers loading and setting up the UX
- */
- @Override
- public void setCallback(Callback callback) {
- super.setCallback(callback);
- if (mStarted) {
- configureEditor();
- loadSettings();
- }
- }
-
- /**
* Configure the editor for the account type
*/
private void configureEditor() {
final Account account = mSetupData.getAccount();
if (account == null || account.mHostAuthRecv == null) {
- LogUtils.e(Logging.LOG_TAG,
+ LogUtils.e(LogUtils.TAG,
"null account or host auth. account null: %b host auth null: %b",
account == null, account == null || account.mHostAuthRecv == null);
return;
@@ -392,8 +340,8 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
if (mLoaded) return;
final Account account = mSetupData.getAccount();
- final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
- mServiceInfo = EmailServiceUtils.getServiceInfo(mContext, recvAuth.mProtocol);
+ final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mAppContext);
+ mServiceInfo = EmailServiceUtils.getServiceInfo(mAppContext, recvAuth.mProtocol);
mAuthenticationView.setAuthInfo(mServiceInfo.offerOAuth, recvAuth);
if (mAuthenticationLabel != null) {
if (mServiceInfo.offerOAuth) {
@@ -432,7 +380,7 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
flags |= HostAuth.FLAG_SSL;
}
// Strip out any flags that are not related to security type.
- int securityTypeFlags = (flags & (HostAuth.FLAG_SSL | HostAuth.FLAG_TLS | HostAuth.FLAG_NONE));
+ int securityTypeFlags = (flags & HostAuth.FLAG_TRANSPORTSECURITY_MASK);
SpinnerOption.setSpinnerOptionValue(mSecurityTypeView, securityTypeFlags);
final String hostname = recvAuth.mAddress;
@@ -466,7 +414,7 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
}
private int getPortFromSecurityType(boolean useSsl) {
- final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(mContext,
+ final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(mAppContext,
mSetupData.getAccount().mHostAuthRecv.mProtocol);
return useSsl ? info.portSsl : info.port;
}
@@ -483,7 +431,7 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
mClientCertificateSelector.setVisibility(mode);
String deviceId = "";
try {
- deviceId = Device.getDeviceId(mContext);
+ deviceId = Device.getDeviceId(mAppContext);
} catch (IOException e) {
// Not required
}
@@ -500,44 +448,72 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
onUseSslChanged(sslSelected);
}
+ private static class SaveSettingsLoader extends MailAsyncTaskLoader<Boolean> {
+ private final SetupDataFragment mSetupData;
+ private final boolean mSettingsMode;
+
+ private SaveSettingsLoader(Context context, SetupDataFragment setupData,
+ boolean settingsMode) {
+ super(context);
+ mSetupData = setupData;
+ mSettingsMode = settingsMode;
+ }
+
+ @Override
+ public Boolean loadInBackground() {
+ if (mSettingsMode) {
+ saveSettingsAfterEdit(getContext(), mSetupData);
+ } else {
+ saveSettingsAfterSetup(getContext(), mSetupData);
+ }
+ return true;
+ }
+
+ @Override
+ protected void onDiscardResult(Boolean result) {}
+ }
+
+ @Override
+ public Loader<Boolean> getSaveSettingsLoader() {
+ return new SaveSettingsLoader(mAppContext, mSetupData, mSettingsMode);
+ }
+
/**
* Entry point from Activity after editing settings and verifying them. Must be FLOW_MODE_EDIT.
* Note, we update account here (as well as the account.mHostAuthRecv) because we edit
* account's delete policy here.
* Blocking - do not call from UI Thread.
*/
- @Override
- public void saveSettingsAfterEdit() {
- final Account account = mSetupData.getAccount();
- account.update(mContext, account.toContentValues());
+ public static void saveSettingsAfterEdit(Context context, SetupDataFragment setupData) {
+ final Account account = setupData.getAccount();
+ account.update(context, account.toContentValues());
final Credential cred = account.mHostAuthRecv.mCredential;
if (cred != null) {
if (cred.isSaved()) {
- cred.update(mContext, cred.toContentValues());
+ cred.update(context, cred.toContentValues());
} else {
- cred.save(mContext);
+ cred.save(context);
account.mHostAuthRecv.mCredentialKey = cred.mId;
}
}
- account.mHostAuthRecv.update(mContext, account.mHostAuthRecv.toContentValues());
+ account.mHostAuthRecv.update(context, account.mHostAuthRecv.toContentValues());
// Update the backup (side copy) of the accounts
- AccountBackupRestore.backup(mContext);
+ AccountBackupRestore.backup(context);
}
/**
* Entry point from Activity after entering new settings and verifying them. For setup mode.
*/
- @Override
- public void saveSettingsAfterSetup() {
- final Account account = mSetupData.getAccount();
- final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
- final HostAuth sendAuth = account.getOrCreateHostAuthSend(mContext);
+ public static void saveSettingsAfterSetup(Context context, SetupDataFragment setupData) {
+ final Account account = setupData.getAccount();
+ final HostAuth recvAuth = account.getOrCreateHostAuthRecv(context);
+ final HostAuth sendAuth = account.getOrCreateHostAuthSend(context);
// Set the username and password for the outgoing settings to the username and
// password the user just set for incoming. Use the verified host address to try and
// pick a smarter outgoing address.
final String hostName =
- AccountSettingsUtils.inferServerName(mContext, recvAuth.mAddress, null, "smtp");
+ AccountSettingsUtils.inferServerName(context, recvAuth.mAddress, null, "smtp");
sendAuth.setLogin(recvAuth.mLogin, recvAuth.mPassword);
sendAuth.setConnection(sendAuth.mProtocol, hostName, sendAuth.mPort, sendAuth.mFlags);
}
@@ -546,7 +522,7 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
* Entry point from Activity, when "next" button is clicked
*/
@Override
- public void onNext() {
+ public void collectUserInput() {
final Account account = mSetupData.getAccount();
// Make sure delete policy is an valid option before using it; otherwise, the results are
@@ -556,9 +532,9 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
(Integer) ((SpinnerOption) mDeletePolicyView.getSelectedItem()).value);
}
- final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
+ final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mAppContext);
final String userName = mUsernameView.getText().toString().trim();
- final String userPassword = mAuthenticationView.getPassword().toString();
+ final String userPassword = mAuthenticationView.getPassword();
recvAuth.setLogin(userName, userPassword);
if (!TextUtils.isEmpty(mAuthenticationView.getOAuthProvider())) {
Credential cred = recvAuth.getOrCreateCredential(getActivity());
@@ -571,7 +547,7 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
serverPort = Integer.parseInt(mPortView.getText().toString().trim());
} catch (NumberFormatException e) {
serverPort = getPortFromSecurityType(getSslSelected());
- LogUtils.d(Logging.LOG_TAG, "Non-integer server port; using '" + serverPort + "'");
+ LogUtils.d(LogUtils.TAG, "Non-integer server port; using '" + serverPort + "'");
}
final int securityType =
(Integer) ((SpinnerOption) mSecurityTypeView.getSelectedItem()).value;
@@ -584,8 +560,8 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
}
recvAuth.mClientCertAlias = mClientCertificateSelector.getCertificate();
- mCallback.onProceedNext(SetupDataFragment.CHECK_INCOMING, this);
- clearButtonBounce();
+ final Callback callback = (Callback) getActivity();
+ callback.onAccountServerUIComplete(SetupDataFragment.CHECK_INCOMING);
}
@Override
@@ -604,16 +580,6 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
return deletePolicyChanged || super.haveSettingsChanged();
}
- /**
- * Implements AccountCheckSettingsFragment.Callbacks
- */
- @Override
- public void onAutoDiscoverComplete(int result, SetupDataFragment setupData) {
- mSetupData = setupData;
- final AccountSetupIncoming activity = (AccountSetupIncoming) getActivity();
- activity.onAutoDiscoverComplete(result, setupData);
- }
-
@Override
public void onValidateStateChanged() {
validateFields();
@@ -621,10 +587,11 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
@Override
public void onRequestSignIn() {
- // Launch the signin activity.
- // TODO: at some point we should just use the sign in fragment on the main setup activity.
- final Intent intent = new Intent(getActivity(), SignInActivity.class);
- intent.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, mSetupData);
+ // Launch the credentials activity.
+ final String protocol =
+ mSetupData.getAccount().getOrCreateHostAuthRecv(mAppContext).mProtocol;
+ final Intent intent = AccountCredentials.getAccountCredentialsIntent(getActivity(),
+ mUsernameView.getText().toString(), protocol);
startActivityForResult(intent, SIGN_IN_REQUEST);
}
@@ -646,20 +613,8 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
} else if (requestCode == SIGN_IN_REQUEST && resultCode == Activity.RESULT_OK) {
final Account account = mSetupData.getAccount();
final HostAuth recvAuth = account.getOrCreateHostAuthRecv(getActivity());
- final String password = data.getStringExtra(SignInActivity.EXTRA_PASSWORD);
- if (!TextUtils.isEmpty(password)) {
- recvAuth.mPassword = password;
- recvAuth.removeCredential();
- } else {
- Credential cred = recvAuth.getOrCreateCredential(getActivity());
- cred.mProviderId = data.getStringExtra(SignInActivity.EXTRA_OAUTH_PROVIDER);
- cred.mAccessToken = data.getStringExtra(SignInActivity.EXTRA_OAUTH_ACCESS_TOKEN);
- cred.mRefreshToken = data.getStringExtra(SignInActivity.EXTRA_OAUTH_REFRESH_TOKEN);
- cred.mExpiration = System.currentTimeMillis() +
- data.getIntExtra(SignInActivity.EXTRA_OAUTH_EXPIRES_IN_SECONDS, 0) *
- DateUtils.SECOND_IN_MILLIS;
- recvAuth.mPassword = null;
- }
+ AccountSetupCredentialsFragment.populateHostAuthWithResults(mAppContext, recvAuth,
+ data.getExtras());
mAuthenticationView.setAuthInfo(mServiceInfo.offerOAuth, recvAuth);
}
}
diff --git a/src/com/android/email/activity/setup/AccountSetupNamesFragment.java b/src/com/android/email/activity/setup/AccountSetupNamesFragment.java
index 393f4c2b9..0e52ad307 100644
--- a/src/com/android/email/activity/setup/AccountSetupNamesFragment.java
+++ b/src/com/android/email/activity/setup/AccountSetupNamesFragment.java
@@ -16,7 +16,6 @@
package com.android.email.activity.setup;
-import android.app.Fragment;
import android.app.LoaderManager;
import android.content.Context;
import android.content.CursorLoader;
@@ -38,11 +37,19 @@ import com.android.email.activity.UiUtilities;
import com.android.email.service.EmailServiceUtils;
import com.android.emailcommon.provider.Account;
-public class AccountSetupNamesFragment extends Fragment {
+public class AccountSetupNamesFragment extends AccountSetupFragment {
private EditText mDescription;
private EditText mName;
private boolean mRequiresName = true;
+ public interface Callback extends AccountSetupFragment.Callback {
+
+ }
+
+ public static AccountSetupNamesFragment newInstance() {
+ return new AccountSetupNamesFragment();
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -158,10 +165,10 @@ public class AccountSetupNamesFragment extends Fragment {
mName.setError(null);
}
}
- final AccountSetupFinal activity = (AccountSetupFinal) getActivity();
- if (activity != null) {
+ final Callback callback = (Callback) getActivity();
+ if (callback != null) {
// If we're not attached to the activity, this state probably doesn't need updating
- activity.setNextButtonEnabled(enableNextButton);
+ callback.setNextButtonEnabled(enableNextButton);
}
}
diff --git a/src/com/android/email/activity/setup/AccountSetupNoteDialogFragment.java b/src/com/android/email/activity/setup/AccountSetupNoteDialogFragment.java
new file mode 100644
index 000000000..c889e9978
--- /dev/null
+++ b/src/com/android/email/activity/setup/AccountSetupNoteDialogFragment.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.email.activity.setup;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import com.android.email.R;
+
+public class AccountSetupNoteDialogFragment extends DialogFragment {
+ public static final String TAG = "NoteDialogFragment";
+
+ // Argument bundle keys
+ private static final String BUNDLE_KEY_NOTE = "NoteDialogFragment.Note";
+
+ public static interface Callback {
+ void onNoteDialogComplete();
+ }
+
+ // Public no-args constructor needed for fragment re-instantiation
+ public AccountSetupNoteDialogFragment() {}
+
+ /**
+ * Create the dialog with parameters
+ */
+ public static AccountSetupNoteDialogFragment newInstance(String note) {
+ final AccountSetupNoteDialogFragment f = new AccountSetupNoteDialogFragment();
+ final Bundle b = new Bundle(1);
+ b.putString(BUNDLE_KEY_NOTE, note);
+ f.setArguments(b);
+ return f;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+ final String note = getArguments().getString(BUNDLE_KEY_NOTE);
+
+ return new AlertDialog.Builder(context)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setTitle(android.R.string.dialog_alert_title)
+ .setMessage(note)
+ .setPositiveButton(
+ R.string.okay_action,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final Callback a = (Callback) getActivity();
+ a.onNoteDialogComplete();
+ dismiss();
+ }
+ })
+ .setNegativeButton(
+ context.getString(R.string.cancel_action),
+ null)
+ .create();
+ }
+}
diff --git a/src/com/android/email/activity/setup/AccountSetupOptionsFragment.java b/src/com/android/email/activity/setup/AccountSetupOptionsFragment.java
index b89ab3fd0..272045503 100644
--- a/src/com/android/email/activity/setup/AccountSetupOptionsFragment.java
+++ b/src/com/android/email/activity/setup/AccountSetupOptionsFragment.java
@@ -16,7 +16,6 @@
package com.android.email.activity.setup;
-import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -32,7 +31,7 @@ import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.Policy;
import com.android.emailcommon.service.SyncWindow;
-public class AccountSetupOptionsFragment extends Fragment {
+public class AccountSetupOptionsFragment extends AccountSetupFragment {
private Spinner mCheckFrequencyView;
private Spinner mSyncWindowView;
private CheckBox mNotifyView;
@@ -45,6 +44,14 @@ public class AccountSetupOptionsFragment extends Fragment {
/** Default sync window for new EAS accounts */
private static final int SYNC_WINDOW_EAS_DEFAULT = SyncWindow.SYNC_WINDOW_1_WEEK;
+ public interface Callback extends AccountSetupFragment.Callback {
+
+ }
+
+ public static AccountSetupOptionsFragment newInstance() {
+ return new AccountSetupOptionsFragment();
+ }
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
diff --git a/src/com/android/email/activity/setup/AccountSetupOutgoing.java b/src/com/android/email/activity/setup/AccountSetupOutgoing.java
deleted file mode 100644
index 4d7327420..000000000
--- a/src/com/android/email/activity/setup/AccountSetupOutgoing.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.email.activity.setup;
-
-import com.android.email.R;
-import com.android.email.activity.UiUtilities;
-
-import android.app.Activity;
-import android.app.FragmentTransaction;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.ImageButton;
-
-/**
- * Provides setup flow for SMTP (for IMAP/POP accounts).
- *
- * Uses AccountSetupOutgoingFragment for primary UI. Uses AccountCheckSettingsFragment to validate
- * the settings as entered. If the account is OK, proceeds to AccountSetupOptions.
- */
-public class AccountSetupOutgoing extends AccountSetupActivity
- implements AccountSetupOutgoingFragment.Callback, OnClickListener {
-
- /* package */ AccountSetupOutgoingFragment mFragment;
- private ImageButton mNextButton;
- /* package */ boolean mNextButtonEnabled;
-
- public static void actionOutgoingSettings(Activity fromActivity, SetupDataFragment setupData) {
- Intent intent = new Intent(fromActivity, AccountSetupOutgoing.class);
- intent.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, setupData);
- fromActivity.startActivity(intent);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.account_setup_outgoing);
-
- mFragment = (AccountSetupOutgoingFragment)
- getFragmentManager().findFragmentById(R.id.setup_fragment);
-
- // Configure fragment
- mFragment.setCallback(this);
-
- mNextButton = UiUtilities.getView(this, R.id.next);
- mNextButton.setOnClickListener(this);
- UiUtilities.getView(this, R.id.previous).setOnClickListener(this);
- }
-
- /**
- * Implements View.OnClickListener
- */
- @Override
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.next:
- mFragment.onNext();
- break;
- case R.id.previous:
- onBackPressed();
- break;
- }
- }
-
- /**
- * Implements AccountSetupOugoingFragment.Callback
- *
- * Launches the account checker. Positive results are reported to onCheckSettingsOk().
- */
- @Override
- public void onProceedNext(int checkMode, AccountServerBaseFragment target) {
- final AccountCheckSettingsFragment checkerFragment =
- AccountCheckSettingsFragment.newInstance(checkMode, target);
- final FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.add(checkerFragment, AccountCheckSettingsFragment.TAG);
- transaction.addToBackStack("back");
- transaction.commit();
- }
-
- /**
- * Implements AccountSetupOugoingFragment.Callback
- */
- @Override
- public void onEnableProceedButtons(boolean enable) {
- mNextButtonEnabled = enable;
- mNextButton.setEnabled(enable);
- }
-
- /**
- * Implements AccountServerBaseFragment.Callback
- *
- * If the checked settings are OK, proceed to options screen
- */
- @Override
- public void onCheckSettingsComplete(int result, SetupDataFragment setupData) {
- mSetupData = setupData;
- if (result == AccountCheckSettingsFragment.CHECK_SETTINGS_OK) {
- AccountSetupFinal.actionFinal(this, mSetupData);
- finish();
- }
- }
-}
diff --git a/src/com/android/email/activity/setup/AccountSetupOutgoingFragment.java b/src/com/android/email/activity/setup/AccountSetupOutgoingFragment.java
index 3583f4fd5..c8e1d0b2a 100644
--- a/src/com/android/email/activity/setup/AccountSetupOutgoingFragment.java
+++ b/src/com/android/email/activity/setup/AccountSetupOutgoingFragment.java
@@ -19,7 +19,7 @@ package com.android.email.activity.setup;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
-import android.net.Uri;
+import android.content.Loader;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
@@ -41,15 +41,11 @@ import com.android.email.R;
import com.android.email.activity.UiUtilities;
import com.android.email.activity.setup.AuthenticationView.AuthenticationCallback;
import com.android.email.provider.AccountBackupRestore;
-import com.android.email.service.EmailServiceUtils;
-import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
-import com.android.email2.ui.MailActivityEmail;
-import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.Credential;
import com.android.emailcommon.provider.HostAuth;
-import com.android.emailcommon.utility.CertificateRequestor;
import com.android.emailcommon.utility.Utility;
+import com.android.mail.ui.MailAsyncTaskLoader;
import com.android.mail.utils.LogUtils;
/**
@@ -77,9 +73,14 @@ public class AccountSetupOutgoingFragment extends AccountServerBaseFragment
private Spinner mSecurityTypeView;
// Support for lifecycle
- private boolean mStarted;
private boolean mLoaded;
+ public static AccountSetupOutgoingFragment newInstance(boolean settingsMode) {
+ final AccountSetupOutgoingFragment f = new AccountSetupOutgoingFragment();
+ f.setArguments(getArgs(settingsMode));
+ return f;
+ }
+
// Public no-args constructor needed for fragment re-instantiation
public AccountSetupOutgoingFragment() {}
@@ -89,9 +90,6 @@ public class AccountSetupOutgoingFragment extends AccountServerBaseFragment
*/
@Override
public void onCreate(Bundle savedInstanceState) {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onCreate");
- }
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
@@ -103,9 +101,6 @@ public class AccountSetupOutgoingFragment extends AccountServerBaseFragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onCreateView");
- }
final int layoutId = mSettingsMode
? R.layout.account_settings_outgoing_fragment
: R.layout.account_setup_outgoing_fragment;
@@ -192,22 +187,7 @@ public class AccountSetupOutgoingFragment extends AccountServerBaseFragment
@Override
public void onActivityCreated(Bundle savedInstanceState) {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onActivityCreated");
- }
super.onActivityCreated(savedInstanceState);
- }
-
- /**
- * Called when the Fragment is visible to the user.
- */
- @Override
- public void onStart() {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onStart");
- }
- super.onStart();
- mStarted = true;
loadSettings();
}
@@ -216,72 +196,34 @@ public class AccountSetupOutgoingFragment extends AccountServerBaseFragment
*/
@Override
public void onResume() {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onResume");
- }
super.onResume();
validateFields();
}
@Override
- public void onPause() {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onPause");
- }
- super.onPause();
- }
-
- /**
- * Called when the Fragment is no longer started.
- */
- @Override
- public void onStop() {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onStop");
- }
- super.onStop();
- mStarted = false;
- }
-
- /**
- * Called when the fragment is no longer in use.
- */
- @Override
- public void onDestroy() {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onDestroy");
- }
- super.onDestroy();
- }
-
- @Override
public void onSaveInstanceState(Bundle outState) {
- if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
- LogUtils.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onSaveInstanceState");
- }
super.onSaveInstanceState(outState);
outState.putBoolean(STATE_KEY_LOADED, mLoaded);
}
/**
- * Activity provides callbacks here. This also triggers loading and setting up the UX
- */
- @Override
- public void setCallback(Callback callback) {
- super.setCallback(callback);
- if (mStarted) {
- loadSettings();
- }
- }
-
- /**
* Load the current settings into the UI
*/
private void loadSettings() {
if (mLoaded) return;
- final HostAuth sendAuth = mSetupData.getAccount().getOrCreateHostAuthSend(mContext);
+ final HostAuth sendAuth = mSetupData.getAccount().getOrCreateHostAuthSend(mAppContext);
+ if (!mSetupData.isOutgoingCredLoaded()) {
+ sendAuth.setUserName(mSetupData.getEmail());
+ AccountSetupCredentialsFragment.populateHostAuthWithResults(mAppContext, sendAuth,
+ mSetupData.getCredentialResults());
+ final String[] emailParts = mSetupData.getEmail().split("@");
+ final String domain = emailParts[1];
+ sendAuth.setConnection(sendAuth.mProtocol, domain, HostAuth.PORT_UNKNOWN,
+ HostAuth.FLAG_NONE);
+ mSetupData.setOutgoingCredLoaded(true);
+ }
if ((sendAuth.mFlags & HostAuth.FLAG_AUTHENTICATE) != 0) {
final String username = sendAuth.mLogin;
if (username != null) {
@@ -352,45 +294,75 @@ public class AccountSetupOutgoingFragment extends AccountServerBaseFragment
mPortView.setText(Integer.toString(port));
}
+ private static class SaveSettingsLoader extends MailAsyncTaskLoader<Boolean> {
+ private final SetupDataFragment mSetupData;
+ private final boolean mSettingsMode;
+
+ private SaveSettingsLoader(Context context, SetupDataFragment setupData,
+ boolean settingsMode) {
+ super(context);
+ mSetupData = setupData;
+ mSettingsMode = settingsMode;
+ }
+
+ @Override
+ public Boolean loadInBackground() {
+ if (mSettingsMode) {
+ saveSettingsAfterEdit(getContext(), mSetupData);
+ } else {
+ saveSettingsAfterSetup(getContext(), mSetupData);
+ }
+ return true;
+ }
+
+ @Override
+ protected void onDiscardResult(Boolean result) {}
+ }
+
+ @Override
+ public Loader<Boolean> getSaveSettingsLoader() {
+ return new SaveSettingsLoader(mAppContext, mSetupData, mSettingsMode);
+ }
+
/**
* Entry point from Activity after editing settings and verifying them. Must be FLOW_MODE_EDIT.
* Blocking - do not call from UI Thread.
*/
- @Override
- public void saveSettingsAfterEdit() {
- final Account account = mSetupData.getAccount();
- final Credential cred = account.mHostAuthRecv.mCredential;
+ public static void saveSettingsAfterEdit(Context context, SetupDataFragment setupData) {
+ final Account account = setupData.getAccount();
+ final Credential cred = account.mHostAuthSend.mCredential;
if (cred != null) {
if (cred.isSaved()) {
- cred.update(mContext, cred.toContentValues());
+ cred.update(context, cred.toContentValues());
} else {
- cred.save(mContext);
- account.mHostAuthRecv.mCredentialKey = cred.mId;
+ cred.save(context);
+ account.mHostAuthSend.mCredentialKey = cred.mId;
}
}
- account.mHostAuthSend.update(mContext, account.mHostAuthSend.toContentValues());
+ account.mHostAuthSend.update(context, account.mHostAuthSend.toContentValues());
// Update the backup (side copy) of the accounts
- AccountBackupRestore.backup(mContext);
+ AccountBackupRestore.backup(context);
}
/**
* Entry point from Activity after entering new settings and verifying them. For setup mode.
*/
- @Override
- public void saveSettingsAfterSetup() {
+ @SuppressWarnings("unused")
+ public static void saveSettingsAfterSetup(Context context, SetupDataFragment setupData) {
+ // No need to do anything here
}
/**
* Entry point from Activity, when "next" button is clicked
*/
@Override
- public void onNext() {
+ public void collectUserInput() {
final Account account = mSetupData.getAccount();
- final HostAuth sendAuth = account.getOrCreateHostAuthSend(mContext);
+ final HostAuth sendAuth = account.getOrCreateHostAuthSend(mAppContext);
if (mRequireLoginView.isChecked()) {
final String userName = mUsernameView.getText().toString().trim();
- final String userPassword = mAuthenticationView.getPassword().toString();
+ final String userPassword = mAuthenticationView.getPassword();
sendAuth.setLogin(userName, userPassword);
} else {
sendAuth.setLogin(null, null);
@@ -402,15 +374,15 @@ public class AccountSetupOutgoingFragment extends AccountServerBaseFragment
serverPort = Integer.parseInt(mPortView.getText().toString().trim());
} catch (NumberFormatException e) {
serverPort = getPortFromSecurityType();
- LogUtils.d(Logging.LOG_TAG, "Non-integer server port; using '" + serverPort + "'");
+ LogUtils.d(LogUtils.TAG, "Non-integer server port; using '" + serverPort + "'");
}
final int securityType =
(Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
sendAuth.setConnection(mBaseScheme, serverAddress, serverPort, securityType);
sendAuth.mDomain = null;
- mCallback.onProceedNext(SetupDataFragment.CHECK_OUTGOING, this);
- clearButtonBounce();
+ final Callback callback = (Callback) getActivity();
+ callback.onAccountServerUIComplete(SetupDataFragment.CHECK_OUTGOING);
}
@Override
@@ -420,10 +392,22 @@ public class AccountSetupOutgoingFragment extends AccountServerBaseFragment
@Override
public void onRequestSignIn() {
- // Launch the signin activity.
- // TODO: at some point we should just use the sign in fragment on the main setup activity.
- final Intent intent = new Intent(getActivity(), SignInActivity.class);
- intent.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, mSetupData);
+ // Launch the credential activity.
+ final String protocol =
+ mSetupData.getAccount().getOrCreateHostAuthSend(mAppContext).mProtocol;
+ final Intent intent = AccountCredentials.getAccountCredentialsIntent(getActivity(),
+ mUsernameView.getText().toString(), protocol);
startActivityForResult(intent, SIGN_IN_REQUEST);
}
+
+ @Override
+ public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
+ if (requestCode == SIGN_IN_REQUEST && resultCode == Activity.RESULT_OK) {
+ final Account account = mSetupData.getAccount();
+ final HostAuth sendAuth = account.getOrCreateHostAuthSend(getActivity());
+ AccountSetupCredentialsFragment.populateHostAuthWithResults(mAppContext, sendAuth,
+ data.getExtras());
+ mAuthenticationView.setAuthInfo(true, sendAuth);
+ }
+ }
}
diff --git a/src/com/android/email/activity/setup/AccountSetupType.java b/src/com/android/email/activity/setup/AccountSetupType.java
deleted file mode 100644
index 83b345f2f..000000000
--- a/src/com/android/email/activity/setup/AccountSetupType.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.email.activity.setup;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.RelativeLayout;
-import android.widget.RelativeLayout.LayoutParams;
-
-import com.android.email.R;
-import com.android.email.activity.UiUtilities;
-import com.android.email.service.EmailServiceUtils;
-import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
-import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.HostAuth;
-import com.android.emailcommon.utility.Utility;
-import com.android.mail.utils.LogUtils;
-
-/**
- * Prompts the user to select an account type. The account type, along with the
- * passed in email address, password and makeDefault are then passed on to the
- * AccountSetupIncoming activity.
- */
-public class AccountSetupType extends AccountSetupActivity implements OnClickListener {
-
- private boolean mButtonPressed;
-
- public static void actionSelectAccountType(Activity fromActivity, SetupDataFragment setupData) {
- final Intent i = new ForwardingIntent(fromActivity, AccountSetupType.class);
- i.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, setupData);
- fromActivity.startActivity(i);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- final String accountType = mSetupData.getFlowAccountType();
- // If we're in account setup flow mode, see if there's just one protocol that matches
- if (mSetupData.getFlowMode() == SetupDataFragment.FLOW_MODE_ACCOUNT_MANAGER) {
- int matches = 0;
- String protocol = null;
- for (EmailServiceInfo info: EmailServiceUtils.getServiceInfoList(this)) {
- if (info.accountType.equals(accountType)) {
- protocol = info.protocol;
- matches++;
- }
- }
- // If so, select it...
- if (matches == 1) {
- onSelect(protocol);
- return;
- }
- }
-
- // Otherwise proceed into this screen
- setContentView(R.layout.account_setup_account_type);
- final ViewGroup parent = UiUtilities.getView(this, R.id.accountTypes);
- View lastView = parent.getChildAt(0);
- int i = 1;
- for (EmailServiceInfo info: EmailServiceUtils.getServiceInfoList(this)) {
- if (EmailServiceUtils.isServiceAvailable(this, info.protocol)) {
- // If we're looking for a specific account type, reject others
- // Don't show types with "hide" set
- if (info.hide || (accountType != null && !accountType.equals(info.accountType))) {
- continue;
- }
- LayoutInflater.from(this).inflate(R.layout.account_type, parent);
- final Button button = (Button)parent.getChildAt(i);
- if (parent instanceof RelativeLayout) {
- final LayoutParams params = (LayoutParams)button.getLayoutParams();
- params.addRule(RelativeLayout.BELOW, lastView.getId());
- }
- button.setId(i);
- button.setTag(info.protocol);
- button.setText(info.name);
- button.setOnClickListener(this);
- lastView = button;
- i++;
- // TODO: Remember vendor overlay for exchange name
- }
- }
- final Button previousButton = (Button) findViewById(R.id.previous); // xlarge only
- if (previousButton != null) previousButton.setOnClickListener(this);
- }
-
- /**
- * The user has selected an exchange account type. Set the mail delete policy here, because
- * there is no UI (for exchange), and switch the default sync interval to "push".
- */
- private void onSelect(String protocol) {
- final Account account = mSetupData.getAccount();
- final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
- recvAuth.setConnection(protocol, recvAuth.mAddress, recvAuth.mPort, recvAuth.mFlags);
- final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(this, protocol);
-
- new DuplicateCheckTask(account.mEmailAddress, info.accountType)
- .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
- private void onProceedNext() {
- final Account account = mSetupData.getAccount();
- final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
- final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(this, recvAuth.mProtocol);
- if (info.usesAutodiscover) {
- mSetupData.setCheckSettingsMode(SetupDataFragment.CHECK_AUTODISCOVER);
- } else {
- mSetupData.setCheckSettingsMode(
- SetupDataFragment.CHECK_INCOMING |
- (info.usesSmtp ? SetupDataFragment.CHECK_OUTGOING : 0));
- }
- recvAuth.mLogin = recvAuth.mLogin + "@" + recvAuth.mAddress;
- AccountSetupBasics.setDefaultsForProtocol(this, account);
-
- // XXX this launches the incoming activity. We should only be doing this
- // if we are in full on manual mode I think.
- AccountSetupIncoming.actionIncomingSettings(this, mSetupData);
-
- // XXX This launches the signin activity.
-// final Intent intent = new Intent(this, SignInActivity.class);
-// intent.putExtra(SignInActivity.EXTRA_FLOW_MODE_INITIAL, true);
-// intent.putExtra(SignInActivity.EXTRA_MANUAL_SETUP, false);
-// intent.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, mSetupData);
-// startActivity(intent);
- // Back from the incoming screen returns to AccountSetupBasics
- finish();
- }
-
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.previous:
- finish();
- break;
- default:
- if (!mButtonPressed) {
- mButtonPressed = true;
- onSelect((String)v.getTag());
- }
- break;
- }
- }
-
- private class DuplicateCheckTask extends AsyncTask<Void, Void, String> {
- private final String mAddress;
- private final String mAuthority;
-
- public DuplicateCheckTask(String address, String authority) {
- mAddress = address;
- mAuthority = authority;
- }
-
- @Override
- protected String doInBackground(Void... params) {
- return Utility.findExistingAccount(AccountSetupType.this, mAuthority, mAddress);
- }
-
- @Override
- protected void onPostExecute(String duplicateAccountName) {
- mButtonPressed = false;
- if (duplicateAccountName != null) {
- // Show duplicate account warning
- final DuplicateAccountDialogFragment dialogFragment =
- DuplicateAccountDialogFragment.newInstance(duplicateAccountName);
- dialogFragment.show(AccountSetupType.this.getFragmentManager(),
- DuplicateAccountDialogFragment.TAG);
- } else {
- // Otherwise, proceed with the save/check
- onProceedNext();
- }
- }
-
- @Override
- protected void onCancelled(String s) {
- mButtonPressed = false;
- LogUtils.d(LogUtils.TAG, "Duplicate account check cancelled (AccountSetupType)");
- }
- }
-}
diff --git a/src/com/android/email/activity/setup/AccountSetupTypeFragment.java b/src/com/android/email/activity/setup/AccountSetupTypeFragment.java
new file mode 100644
index 000000000..1bfd536cc
--- /dev/null
+++ b/src/com/android/email/activity/setup/AccountSetupTypeFragment.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.email.activity.setup;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.RelativeLayout;
+
+import com.android.email.R;
+import com.android.email.activity.UiUtilities;
+import com.android.email.service.EmailServiceUtils;
+
+public class AccountSetupTypeFragment extends AccountSetupFragment
+ implements View.OnClickListener {
+
+ public interface Callback extends AccountSetupFragment.Callback {
+ /**
+ * called when the user has selected a protocol type for the account
+ * @param protocol {@link EmailServiceUtils.EmailServiceInfo#protocol}
+ */
+ void onChooseProtocol(String protocol);
+ }
+
+ public static AccountSetupTypeFragment newInstance() {
+ return new AccountSetupTypeFragment();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ final View view = inflater.inflate(R.layout.account_setup_type_fragment, container, false);
+
+ final Context appContext = inflater.getContext().getApplicationContext();
+
+ final ViewGroup parent = UiUtilities.getView(view, R.id.accountTypes);
+ View lastView = parent.getChildAt(0);
+ int i = 1;
+ for (final EmailServiceUtils.EmailServiceInfo info
+ : EmailServiceUtils.getServiceInfoList(appContext)) {
+ if (EmailServiceUtils.isServiceAvailable(appContext, info.protocol)) {
+ // Don't show types with "hide" set
+ if (info.hide) {
+ continue;
+ }
+ inflater.inflate(R.layout.account_type, parent);
+ final Button button = (Button)parent.getChildAt(i);
+ if (parent instanceof RelativeLayout) {
+ final RelativeLayout.LayoutParams params =
+ (RelativeLayout.LayoutParams)button.getLayoutParams();
+ params.addRule(RelativeLayout.BELOW, lastView.getId());
+ }
+ button.setId(i);
+ button.setTag(info.protocol);
+ button.setText(info.name);
+ button.setOnClickListener(this);
+ lastView = button;
+ i++;
+ }
+ }
+
+ return view;
+ }
+
+ @Override
+ public void onClick(View v) {
+ final String protocol = (String) v.getTag();
+ final Callback callback = (Callback) getActivity();
+ callback.onChooseProtocol(protocol);
+ }
+}
diff --git a/src/com/android/email/activity/setup/CheckSettingsErrorDialogFragment.java b/src/com/android/email/activity/setup/CheckSettingsErrorDialogFragment.java
new file mode 100644
index 000000000..558eedc6d
--- /dev/null
+++ b/src/com/android/email/activity/setup/CheckSettingsErrorDialogFragment.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.email.activity.setup;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import com.android.email.R;
+import com.android.emailcommon.mail.MessagingException;
+import com.android.mail.utils.LogUtils;
+
+public class CheckSettingsErrorDialogFragment extends DialogFragment{
+ public final static String TAG = "CheckSettingsErrorDialog";
+
+ public final static int REASON_OTHER = 0;
+ public final static int REASON_AUTHENTICATION_FAILED = 1;
+ public final static int REASON_CERTIFICATE_REQUIRED = 2;
+
+ // Bundle keys for arguments
+ private final static String ARGS_MESSAGE = "CheckSettingsErrorDialog.Message";
+ private final static String ARGS_REASON = "CheckSettingsErrorDialog.ExceptionId";
+
+ public interface Callback {
+ /**
+ * Called to indicate the user wants to resolve the error by changing the client certificate
+ */
+ void onCheckSettingsErrorDialogEditCertificate();
+
+ /**
+ * Called to indicate the user wants to resolve the error by editing the server settings
+ */
+ void onCheckSettingsErrorDialogEditSettings();
+ }
+
+ public CheckSettingsErrorDialogFragment() {}
+
+ /**
+ * @param reason see REASON_* constants
+ * @param message from {@link #getErrorString(Context, MessagingException)}
+ * @return new instance
+ */
+ public static CheckSettingsErrorDialogFragment newInstance(int reason, String message) {
+ final CheckSettingsErrorDialogFragment fragment = new CheckSettingsErrorDialogFragment();
+ final Bundle arguments = new Bundle(2);
+ arguments.putString(ARGS_MESSAGE, message);
+ arguments.putInt(ARGS_REASON, reason);
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+ final Bundle arguments = getArguments();
+ final String message = arguments.getString(ARGS_MESSAGE);
+ final int reason = arguments.getInt(ARGS_REASON);
+ final Callback callback = (Callback) getActivity();
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context)
+ .setMessage(message)
+ .setCancelable(true);
+
+ // Use a different title when we get
+ // MessagingException.AUTODISCOVER_AUTHENTICATION_FAILED
+ if (reason == REASON_AUTHENTICATION_FAILED) {
+ builder.setTitle(R.string.account_setup_autodiscover_dlg_authfail_title);
+ } else {
+ builder.setIconAttribute(android.R.attr.alertDialogIcon)
+ .setTitle(context.getString(R.string.account_setup_failed_dlg_title));
+ }
+
+ if (reason == REASON_CERTIFICATE_REQUIRED) {
+ // Certificate error - show two buttons so the host fragment can auto pop
+ // into the appropriate flow.
+ builder.setPositiveButton(
+ context.getString(android.R.string.ok),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ callback.onCheckSettingsErrorDialogEditCertificate();
+ }
+ });
+ builder.setNegativeButton(
+ context.getString(android.R.string.cancel),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ callback.onCheckSettingsErrorDialogEditSettings();
+ }
+ });
+
+ } else {
+ // "Normal" error - just use a single "Edit details" button.
+ builder.setPositiveButton(
+ context.getString(R.string.account_setup_failed_dlg_edit_details_action),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ callback.onCheckSettingsErrorDialogEditSettings();
+ }
+ });
+ }
+ return builder.create();
+ }
+
+ public static int getReasonFromException (MessagingException ex) {
+ final int exceptionCode = ex.getExceptionType();
+ switch (exceptionCode) {
+ case MessagingException.AUTODISCOVER_AUTHENTICATION_FAILED:
+ return REASON_AUTHENTICATION_FAILED;
+ case MessagingException.CLIENT_CERTIFICATE_REQUIRED:
+ return REASON_CERTIFICATE_REQUIRED;
+ }
+ return REASON_OTHER;
+ }
+
+ public static String getErrorString(Context context, MessagingException ex) {
+ final int id;
+ String message = ex.getMessage();
+ if (message != null) {
+ message = message.trim();
+ }
+ switch (ex.getExceptionType()) {
+ // The remaining exception types are handled by setting the state to
+ // STATE_CHECK_ERROR (above, default) and conversion to specific error strings.
+ case MessagingException.CERTIFICATE_VALIDATION_ERROR:
+ id = TextUtils.isEmpty(message)
+ ? R.string.account_setup_failed_dlg_certificate_message
+ : R.string.account_setup_failed_dlg_certificate_message_fmt;
+ break;
+ case MessagingException.AUTHENTICATION_FAILED:
+ id = R.string.account_setup_failed_dlg_auth_message;
+ break;
+ case MessagingException.AUTODISCOVER_AUTHENTICATION_FAILED:
+ id = R.string.account_setup_autodiscover_dlg_authfail_message;
+ break;
+ case MessagingException.AUTHENTICATION_FAILED_OR_SERVER_ERROR:
+ id = R.string.account_setup_failed_check_credentials_message;
+ break;
+ case MessagingException.IOERROR:
+ id = R.string.account_setup_failed_ioerror;
+ break;
+ case MessagingException.TLS_REQUIRED:
+ id = R.string.account_setup_failed_tls_required;
+ break;
+ case MessagingException.AUTH_REQUIRED:
+ id = R.string.account_setup_failed_auth_required;
+ break;
+ case MessagingException.SECURITY_POLICIES_UNSUPPORTED:
+ id = R.string.account_setup_failed_security_policies_unsupported;
+ // Belt and suspenders here; there should always be a non-empty array here
+ String[] unsupportedPolicies = (String[]) ex.getExceptionData();
+ if (unsupportedPolicies == null) {
+ LogUtils.w(LogUtils.TAG, "No data for unsupported policies?");
+ break;
+ }
+ // Build a string, concatenating policies we don't support
+ final StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (String policyName: unsupportedPolicies) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(", ");
+ }
+ sb.append(policyName);
+ }
+ message = sb.toString();
+ break;
+ case MessagingException.ACCESS_DENIED:
+ id = R.string.account_setup_failed_access_denied;
+ break;
+ case MessagingException.PROTOCOL_VERSION_UNSUPPORTED:
+ id = R.string.account_setup_failed_protocol_unsupported;
+ break;
+ case MessagingException.GENERAL_SECURITY:
+ id = R.string.account_setup_failed_security;
+ break;
+ case MessagingException.CLIENT_CERTIFICATE_REQUIRED:
+ id = R.string.account_setup_failed_certificate_required;
+ break;
+ case MessagingException.CLIENT_CERTIFICATE_ERROR:
+ id = R.string.account_setup_failed_certificate_inaccessible;
+ break;
+ default:
+ id = TextUtils.isEmpty(message)
+ ? R.string.account_setup_failed_dlg_server_message
+ : R.string.account_setup_failed_dlg_server_message_fmt;
+ break;
+ }
+ return TextUtils.isEmpty(message)
+ ? context.getString(id)
+ : context.getString(id, message);
+ }
+}
diff --git a/src/com/android/email/activity/setup/CheckSettingsProgressDialogFragment.java b/src/com/android/email/activity/setup/CheckSettingsProgressDialogFragment.java
new file mode 100644
index 000000000..661f9792f
--- /dev/null
+++ b/src/com/android/email/activity/setup/CheckSettingsProgressDialogFragment.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.email.activity.setup;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import com.android.email.R;
+
+/**
+ * Simple dialog that shows progress as we work through the settings checks.
+ * This is stateless except for its UI (e.g. current strings) and can be torn down or
+ * recreated at any time without affecting the account checking progress.
+ */
+public class CheckSettingsProgressDialogFragment extends DialogFragment {
+ public final static String TAG = "CheckProgressDialog";
+
+ // Extras for saved instance state
+ private final static String ARGS_PROGRESS_STRING = "CheckProgressDialog.Progress";
+ private final static String ARGS_MODE_INT = "CheckProgressDialog.Mode";
+
+ // UI
+ private String mProgressString;
+
+ public interface Callback {
+ void onCheckSettingsProgressDialogCancel();
+ }
+
+ // Public no-args constructor needed for fragment re-instantiation
+ public CheckSettingsProgressDialogFragment() {}
+
+ /**
+ * Create a dialog that reports progress
+ * @param checkMode check settings mode
+ */
+ public static CheckSettingsProgressDialogFragment newInstance(int checkMode) {
+ final CheckSettingsProgressDialogFragment f = new CheckSettingsProgressDialogFragment();
+ final Bundle b = new Bundle(1);
+ b.putInt(ARGS_MODE_INT, checkMode);
+ f.setArguments(b);
+ return f;
+ }
+
+ /**
+ * Update the progress of an existing dialog
+ * @param progress latest progress to be displayed
+ */
+ protected void updateProgress(int progress) {
+ mProgressString = AccountCheckSettingsFragment.getProgressString(getActivity(), progress);
+ final AlertDialog dialog = (AlertDialog) getDialog();
+ if (dialog != null && mProgressString != null) {
+ dialog.setMessage(mProgressString);
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+ if (savedInstanceState != null) {
+ mProgressString = savedInstanceState.getString(ARGS_PROGRESS_STRING);
+ }
+ if (mProgressString == null) {
+ final int checkMode = getArguments().getInt(ARGS_MODE_INT);
+ final int progress = AccountCheckSettingsFragment.getProgressForMode(checkMode);
+ mProgressString = AccountCheckSettingsFragment.getProgressString(getActivity(),
+ progress);
+ }
+
+ final ProgressDialog dialog = new ProgressDialog(context);
+ dialog.setIndeterminate(true);
+ dialog.setMessage(mProgressString);
+ dialog.setButton(DialogInterface.BUTTON_NEGATIVE,
+ context.getString(R.string.cancel_action),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+
+ final Callback callback = (Callback) getActivity();
+ callback.onCheckSettingsProgressDialogCancel();
+ }
+ });
+ return dialog;
+ }
+
+ /**
+ * Listen for cancellation, which can happen from places other than the
+ * negative button (e.g. touching outside the dialog), and stop the checker
+ */
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ final Callback callback = (Callback) getActivity();
+ callback.onCheckSettingsProgressDialogCancel();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putString(ARGS_PROGRESS_STRING, mProgressString);
+ }
+}
diff --git a/src/com/android/email/activity/setup/OAuthAuthenticationActivity.java b/src/com/android/email/activity/setup/OAuthAuthenticationActivity.java
index 4211e6d07..5e135c665 100644
--- a/src/com/android/email/activity/setup/OAuthAuthenticationActivity.java
+++ b/src/com/android/email/activity/setup/OAuthAuthenticationActivity.java
@@ -33,8 +33,6 @@ import java.io.IOException;
*/
public class OAuthAuthenticationActivity extends Activity implements
LoaderCallbacks<AuthenticationResult> {
- private final static String TAG = Logging.LOG_TAG;
-
public static final String EXTRA_EMAIL_ADDRESS = "email_address";
public static final String EXTRA_PROVIDER = "provider";
public static final String EXTRA_PROVIDER_ID = "provider_id";
diff --git a/src/com/android/email/activity/setup/SecurityRequiredDialogFragment.java b/src/com/android/email/activity/setup/SecurityRequiredDialogFragment.java
new file mode 100644
index 000000000..f0baf7c61
--- /dev/null
+++ b/src/com/android/email/activity/setup/SecurityRequiredDialogFragment.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.email.activity.setup;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import com.android.email.R;
+
+/**
+ * The "security required" error dialog. This is presented whenever an exchange account
+ * reports that it will require security policy control, and provide the user with the
+ * opportunity to accept or deny this.
+ *
+ * If the user clicks OK, calls onSecurityRequiredDialogResultOk(true) which reports back
+ * to the target as if the settings check was "ok". If the user clicks "cancel", calls
+ * onSecurityRequiredDialogResultOk(false) which simply closes the checker (this is the
+ * same as any other failed check.)
+ */
+
+public class SecurityRequiredDialogFragment extends DialogFragment {
+ public final static String TAG = "SecurityRequiredDialog";
+
+ // Bundle keys for arguments
+ private final static String ARGS_HOST_NAME = "SecurityRequiredDialog.HostName";
+
+ public interface Callback {
+
+ /**
+ * Callback for the result of this dialog fragment
+ * @param ok True for OK pressed, false for cancel
+ */
+ void onSecurityRequiredDialogResult(boolean ok);
+ }
+
+ // Public no-args constructor needed for fragment re-instantiation
+ public SecurityRequiredDialogFragment() {}
+
+ public static SecurityRequiredDialogFragment newInstance(String hostName) {
+ final SecurityRequiredDialogFragment fragment = new SecurityRequiredDialogFragment();
+ final Bundle arguments = new Bundle(1);
+ arguments.putString(ARGS_HOST_NAME, hostName);
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+ final Bundle arguments = getArguments();
+ final String hostName = arguments.getString(ARGS_HOST_NAME);
+ final Callback callback = (Callback) getActivity();
+
+ return new AlertDialog.Builder(context)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setTitle(context.getString(R.string.account_setup_security_required_title))
+ .setMessage(context.getString(
+ R.string.account_setup_security_policies_required_fmt, hostName))
+ .setCancelable(true)
+ .setPositiveButton(
+ context.getString(R.string.okay_action),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ callback.onSecurityRequiredDialogResult(true);
+ }
+ })
+ .setNegativeButton(
+ context.getString(R.string.cancel_action),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ callback.onSecurityRequiredDialogResult(false);
+ }
+ })
+ .create();
+ }
+}
diff --git a/src/com/android/email/activity/setup/SetupDataFragment.java b/src/com/android/email/activity/setup/SetupDataFragment.java
index 8a468eef0..48f1be9b0 100644
--- a/src/com/android/email/activity/setup/SetupDataFragment.java
+++ b/src/com/android/email/activity/setup/SetupDataFragment.java
@@ -1,6 +1,5 @@
package com.android.email.activity.setup;
-import android.accounts.AccountAuthenticatorResponse;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Parcel;
@@ -25,6 +24,7 @@ public class SetupDataFragment extends Fragment implements Parcelable {
public static final int FLOW_MODE_FORCE_CREATE = 4;
// The following two modes are used to "pop the stack" and return from the setup flow. We
// either return to the caller (if we're in an account type flow) or go to the message list
+ // TODO: figure out if we still care about these
public static final int FLOW_MODE_RETURN_TO_CALLER = 5;
public static final int FLOW_MODE_RETURN_TO_MESSAGE_LIST = 6;
public static final int FLOW_MODE_RETURN_NO_ACCOUNTS_RESULT = 7;
@@ -35,45 +35,35 @@ public class SetupDataFragment extends Fragment implements Parcelable {
public static final int CHECK_OUTGOING = 2;
public static final int CHECK_AUTODISCOVER = 4;
- private static final String SAVESTATE_FLOWMODE = "flowMode";
- private static final String SAVESTATE_FLOWACCOUNTTYPE = "flowAccountType";
- private static final String SAVESTATE_ACCOUNT = "account";
- private static final String SAVESTATE_USERNAME = "username";
- private static final String SAVESTATE_PASSWORD = "password";
- private static final String SAVESTATE_CHECKSETTINGSMODE = "checkSettingsMode";
- private static final String SAVESTATE_ALLOWAUTODISCOVER = "allowAutoDiscover";
- private static final String SAVESTATE_POLICY = "policy";
- private static final String SAVESTATE_ACCOUNTAUTHENTICATORRESPONSE =
- "accountAuthenticatorResponse";
- private static final String SAVESTATE_REPORT_AUTHENTICATION_ERROR =
- "reportAuthenticationError";
+ private static final String SAVESTATE_FLOWMODE = "SetupDataFragment.flowMode";
+ private static final String SAVESTATE_ACCOUNT = "SetupDataFragment.account";
+ private static final String SAVESTATE_EMAIL = "SetupDataFragment.email";
+ private static final String SAVESTATE_CREDENTIAL = "SetupDataFragment.credential";
+ private static final String SAVESTATE_INCOMING_LOADED = "SetupDataFragment.incomingLoaded";
+ private static final String SAVESTATE_OUTGOING_LOADED = "SetupDataFragment.outgoingLoaded";
+ private static final String SAVESTATE_POLICY = "SetupDataFragment.policy";
// All access will be through getters/setters
private int mFlowMode = FLOW_MODE_NORMAL;
- private String mFlowAccountType;
private Account mAccount;
- private String mUsername;
- private String mPassword;
- private int mCheckSettingsMode = 0;
- private boolean mAllowAutodiscover = true;
- private Policy mPolicy;
- private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null;
- private boolean mReportAccountAuthenticationError = false;
+ private String mEmail;
+ private Bundle mCredentialResults;
+ // These are used to track whether we've preloaded the login credentials into incoming/outgoing
+ // settings. Set them to 'true' by default, and false when we change the credentials or email
+ private boolean mIncomingCredLoaded = true;
+ private boolean mOutgoingCredLoaded = true;
+ // This is accessed off-thread in AccountCheckSettingsFragment
+ private volatile Policy mPolicy;
public interface SetupDataContainer {
public SetupDataFragment getSetupData();
- public void setSetupData(SetupDataFragment setupData);
}
public SetupDataFragment() {
mPolicy = null;
- mAllowAutodiscover = true;
- mCheckSettingsMode = 0;
mAccount = new Account();
- mUsername = null;
- mPassword = null;
- mAccountAuthenticatorResponse = null;
- mReportAccountAuthenticationError = false;
+ mEmail = null;
+ mCredentialResults = null;
}
public SetupDataFragment(int flowMode) {
@@ -81,11 +71,6 @@ public class SetupDataFragment extends Fragment implements Parcelable {
mFlowMode = flowMode;
}
- public SetupDataFragment(int flowMode, String accountType) {
- this(flowMode);
- mFlowAccountType = accountType;
- }
-
public SetupDataFragment(int flowMode, Account account) {
this(flowMode);
mAccount = account;
@@ -95,17 +80,12 @@ public class SetupDataFragment extends Fragment implements Parcelable {
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(SAVESTATE_FLOWMODE, mFlowMode);
- outState.putString(SAVESTATE_FLOWACCOUNTTYPE, mFlowAccountType);
outState.putParcelable(SAVESTATE_ACCOUNT, mAccount);
- outState.putString(SAVESTATE_USERNAME, mUsername);
- outState.putString(SAVESTATE_PASSWORD, mPassword);
- outState.putInt(SAVESTATE_CHECKSETTINGSMODE, mCheckSettingsMode);
- outState.putBoolean(SAVESTATE_ALLOWAUTODISCOVER, mAllowAutodiscover);
+ outState.putString(SAVESTATE_EMAIL, mEmail);
+ outState.putParcelable(SAVESTATE_CREDENTIAL, mCredentialResults);
+ outState.putBoolean(SAVESTATE_INCOMING_LOADED, mIncomingCredLoaded);
+ outState.putBoolean(SAVESTATE_OUTGOING_LOADED, mOutgoingCredLoaded);
outState.putParcelable(SAVESTATE_POLICY, mPolicy);
- outState.putParcelable(SAVESTATE_ACCOUNTAUTHENTICATORRESPONSE,
- mAccountAuthenticatorResponse);
- outState.putBoolean(SAVESTATE_REPORT_AUTHENTICATION_ERROR,
- mReportAccountAuthenticationError);
}
@Override
@@ -113,17 +93,12 @@ public class SetupDataFragment extends Fragment implements Parcelable {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mFlowMode = savedInstanceState.getInt(SAVESTATE_FLOWMODE);
- mFlowAccountType = savedInstanceState.getString(SAVESTATE_FLOWACCOUNTTYPE);
mAccount = savedInstanceState.getParcelable(SAVESTATE_ACCOUNT);
- mUsername = savedInstanceState.getString(SAVESTATE_USERNAME);
- mPassword = savedInstanceState.getString(SAVESTATE_PASSWORD);
- mCheckSettingsMode = savedInstanceState.getInt(SAVESTATE_CHECKSETTINGSMODE);
- mAllowAutodiscover = savedInstanceState.getBoolean(SAVESTATE_ALLOWAUTODISCOVER);
+ mEmail = savedInstanceState.getString(SAVESTATE_EMAIL);
+ mCredentialResults = savedInstanceState.getParcelable(SAVESTATE_CREDENTIAL);
+ mIncomingCredLoaded = savedInstanceState.getBoolean(SAVESTATE_INCOMING_LOADED);
+ mOutgoingCredLoaded = savedInstanceState.getBoolean(SAVESTATE_OUTGOING_LOADED);
mPolicy = savedInstanceState.getParcelable(SAVESTATE_POLICY);
- mAccountAuthenticatorResponse =
- savedInstanceState.getParcelable(SAVESTATE_ACCOUNTAUTHENTICATORRESPONSE);
- mReportAccountAuthenticationError =
- savedInstanceState.getBoolean(SAVESTATE_REPORT_AUTHENTICATION_ERROR, false);
}
setRetainInstance(true);
}
@@ -137,14 +112,6 @@ public class SetupDataFragment extends Fragment implements Parcelable {
mFlowMode = flowMode;
}
- public String getFlowAccountType() {
- return mFlowAccountType;
- }
-
- public void setFlowAccountType(String flowAccountType) {
- mFlowAccountType = flowAccountType;
- }
-
public Account getAccount() {
return mAccount;
}
@@ -153,63 +120,51 @@ public class SetupDataFragment extends Fragment implements Parcelable {
mAccount = account;
}
- public String getUsername() {
- return mUsername;
+ public String getEmail() {
+ return mEmail;
}
- public void setUsername(String username) {
- mUsername = username;
+ public void setEmail(String email) {
+ mEmail = email;
+ mAccount.mEmailAddress = email;
+ mIncomingCredLoaded = false;
+ mOutgoingCredLoaded = false;
}
- public String getPassword() {
- return mPassword;
+ public Bundle getCredentialResults() {
+ return mCredentialResults;
}
- public void setPassword(String password) {
- mPassword = password;
+ public void setCredentialResults(Bundle credentialResults) {
+ mCredentialResults = credentialResults;
+ mIncomingCredLoaded = false;
+ mOutgoingCredLoaded = false;
}
- public int getCheckSettingsMode() {
- return mCheckSettingsMode;
+ public boolean isIncomingCredLoaded() {
+ return mIncomingCredLoaded;
}
- public void setCheckSettingsMode(int checkSettingsMode) {
- mCheckSettingsMode = checkSettingsMode;
+ public void setIncomingCredLoaded(boolean incomingCredLoaded) {
+ mIncomingCredLoaded = incomingCredLoaded;
}
- public boolean isAllowAutodiscover() {
- return mAllowAutodiscover;
+ public boolean isOutgoingCredLoaded() {
+ return mOutgoingCredLoaded;
}
- public void setAllowAutodiscover(boolean allowAutodiscover) {
- mAllowAutodiscover = allowAutodiscover;
+ public void setOutgoingCredLoaded(boolean outgoingCredLoaded) {
+ mOutgoingCredLoaded = outgoingCredLoaded;
}
- public Policy getPolicy() {
+ public synchronized Policy getPolicy() {
return mPolicy;
}
- public void setPolicy(Policy policy) {
+ public synchronized void setPolicy(Policy policy) {
mPolicy = policy;
}
- public AccountAuthenticatorResponse getAccountAuthenticatorResponse() {
- return mAccountAuthenticatorResponse;
- }
-
- public void setAccountAuthenticatorResponse(
- AccountAuthenticatorResponse accountAuthenticatorResponse) {
- mAccountAuthenticatorResponse = accountAuthenticatorResponse;
- }
-
- public boolean getReportAccountAuthenticationError() {
- return mReportAccountAuthenticationError;
- }
-
- public void setReportAccountAuthenticationError(final boolean report) {
- mReportAccountAuthenticationError = report;
- }
-
// Parcelable methods
@Override
public int describeContents() {
@@ -232,47 +187,38 @@ public class SetupDataFragment extends Fragment implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mFlowMode);
- dest.writeString(mFlowAccountType);
dest.writeParcelable(mAccount, 0);
- dest.writeString(mUsername);
- dest.writeString(mPassword);
- dest.writeInt(mCheckSettingsMode);
- dest.writeInt(mAllowAutodiscover ? 1 : 0);
+ dest.writeString(mEmail);
+ dest.writeParcelable(mCredentialResults, 0);
+ dest.writeBooleanArray(new boolean[] {mIncomingCredLoaded, mOutgoingCredLoaded});
dest.writeParcelable(mPolicy, 0);
- dest.writeParcelable(mAccountAuthenticatorResponse, 0);
}
public SetupDataFragment(Parcel in) {
final ClassLoader loader = getClass().getClassLoader();
mFlowMode = in.readInt();
- mFlowAccountType = in.readString();
mAccount = in.readParcelable(loader);
- mUsername = in.readString();
- mPassword = in.readString();
- mCheckSettingsMode = in.readInt();
- mAllowAutodiscover = in.readInt() == 1;
+ mEmail = in.readString();
+ mCredentialResults = in.readParcelable(loader);
+ final boolean[] credsLoaded = in.createBooleanArray();
+ mIncomingCredLoaded = credsLoaded[0];
+ mOutgoingCredLoaded = credsLoaded[1];
mPolicy = in.readParcelable(loader);
- mAccountAuthenticatorResponse = in.readParcelable(loader);
}
- public String debugString() {
+ @Override
+ public String toString() {
final StringBuilder sb = new StringBuilder("SetupData");
sb.append(":acct=");
sb.append(mAccount == null ? "none" :mAccount.mId);
- if (mUsername != null) {
+ if (mEmail != null) {
sb.append(":user=");
- sb.append(mUsername);
+ sb.append(mEmail);
}
- if (mPassword != null) {
- sb.append(":pass=");
- sb.append(mPassword);
+ if (mCredentialResults != null) {
+ sb.append(":cred=");
+ sb.append(mCredentialResults.toString());
}
- sb.append(":a/d=");
- sb.append(mAllowAutodiscover);
- sb.append(":check=");
- if ((mCheckSettingsMode & CHECK_INCOMING) != 0) sb.append("in+");
- if ((mCheckSettingsMode & CHECK_OUTGOING) != 0) sb.append("out+");
- if ((mCheckSettingsMode & CHECK_AUTODISCOVER) != 0) sb.append("a/d");
sb.append(":policy=");
sb.append(mPolicy == null ? "none" : "exists");
return sb.toString();
diff --git a/src/com/android/email/activity/setup/SignInActivity.java b/src/com/android/email/activity/setup/SignInActivity.java
deleted file mode 100644
index bab5c5a53..000000000
--- a/src/com/android/email/activity/setup/SignInActivity.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package com.android.email.activity.setup;
-
-import android.app.FragmentTransaction;
-import android.content.Intent;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.view.View;
-import android.widget.ImageButton;
-
-import com.android.email.R;
-import com.android.email.activity.UiUtilities;
-import com.android.email.service.EmailServiceUtils;
-import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
-import com.android.emailcommon.Logging;
-import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.Credential;
-import com.android.emailcommon.provider.HostAuth;
-import com.android.mail.utils.LogUtils;
-
-public class SignInActivity extends AccountSetupActivity implements SignInFragment.SignInCallback,
- View.OnClickListener, AccountCheckSettingsFragment.Callbacks{
-
- public static final String EXTRA_MANUAL_SETUP = "manual";
- public static final String EXTRA_FLOW_MODE_INITIAL = "initial";
-
- public static final String EXTRA_PASSWORD = "password";
- public static final String EXTRA_OAUTH_PROVIDER = "provider";
- public static final String EXTRA_OAUTH_ACCESS_TOKEN = "accessToken";
- public static final String EXTRA_OAUTH_REFRESH_TOKEN = "refreshToken";
- public static final String EXTRA_OAUTH_EXPIRES_IN_SECONDS = "expiresInSeconds";
-
- private SignInFragment mFragment;
- private ImageButton mNextButton;
- private ImageButton mPrevButton;
-
- @Override
- public void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.sign_in_activity);
- final String emailAddress = mSetupData.getAccount().mEmailAddress;
- mFragment = (SignInFragment)
- getFragmentManager().findFragmentById(R.id.sign_in_fragment);
- mFragment.setEmailAddress(emailAddress);
-
- mFragment.setSignInCallback(this);
- mNextButton = UiUtilities.getView(this, R.id.next);
- mPrevButton = UiUtilities.getView(this, R.id.previous);
- mNextButton.setOnClickListener(this);
- mPrevButton.setOnClickListener(this);
-
- // Assume canceled until we find out otherwise.
- setResult(RESULT_CANCELED);
- }
-
- @Override
- public void onOAuthSignIn(final String providerId, final String accessToken,
- final String refreshToken, final int expiresInSeconds) {
- if (getIntent().getBooleanExtra(EXTRA_FLOW_MODE_INITIAL, false)) {
- // On initial setup, we now try to validate the account.
- final Account account = mSetupData.getAccount();
- final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
- final Credential cred = recvAuth.getOrCreateCredential(this);
- cred.mProviderId = providerId;
- cred.mAccessToken = accessToken;
- cred.mRefreshToken = refreshToken;
- cred.mExpiration = System.currentTimeMillis() +
- expiresInSeconds * DateUtils.SECOND_IN_MILLIS;
-
- // TODO: For now, assume that we will use SSL because that's what
- // gmail wants. This needs to be parameterized from providers.xml
- recvAuth.mFlags |= HostAuth.FLAG_SSL;
- recvAuth.mFlags |= HostAuth.FLAG_OAUTH;
-
- final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
- sendAuth.mCredential = cred;
- sendAuth.mFlags |= HostAuth.FLAG_SSL;
- sendAuth.mFlags |= HostAuth.FLAG_OAUTH;
- startAuthenticationCheck();
- } else {
- // On regular settings, we just return the auth info to the caller.
- final Intent intent = new Intent();
- intent.putExtra(EXTRA_OAUTH_PROVIDER, providerId);
- intent.putExtra(EXTRA_OAUTH_ACCESS_TOKEN, accessToken);
- intent.putExtra(EXTRA_OAUTH_REFRESH_TOKEN, refreshToken);
- intent.putExtra(EXTRA_OAUTH_EXPIRES_IN_SECONDS, expiresInSeconds);
- setResult(RESULT_OK, intent);
- finish();
- }
- }
-
- private void onNext() {
- final String password = mFragment.getPassword();
- // This only applies for password authentication.
- if (getIntent().getBooleanExtra(EXTRA_FLOW_MODE_INITIAL, false)) {
- // On initial setup, we now try to validate the account.
-
- final Account account = mSetupData.getAccount();
- final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
- recvAuth.mPassword = password;
-
- final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
- sendAuth.mPassword = password;
-
- startAuthenticationCheck();
- } else {
- // On regular settings, we just return the auth info to the caller.
- final Intent intent = new Intent();
- intent.putExtra(EXTRA_PASSWORD, password);
- setResult(RESULT_OK, intent);
- finish();
- }
- }
-
- private void startAuthenticationCheck() {
- final AccountCheckSettingsFragment checkerFragment =
- AccountCheckSettingsFragment.newInstance(
- SetupDataFragment.CHECK_INCOMING | SetupDataFragment.CHECK_OUTGOING,
- null);
- final FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.add(checkerFragment, AccountCheckSettingsFragment.TAG);
- transaction.addToBackStack(null);
- transaction.commit();
- }
-
- @Override
- public void onValidate() {
- mNextButton.setEnabled(!TextUtils.isEmpty(mFragment.getPassword()));
- }
-
- @Override
- public void onClick(View view) {
- if (view == mNextButton) {
- onNext();
- } else if (view == mPrevButton) {
- onBackPressed();
- }
- }
-
- /**
- * Implements AccountCheckSettingsFragment.Callbacks
- *
- * This is used in automatic setup mode to jump directly down to the options screen.
- *
- * This is the only case where we finish() this activity but account setup is continuing,
- * so we inhibit reporting any error back to the Account manager.
- */
- @Override
- public void onCheckSettingsComplete(int result, SetupDataFragment setupData) {
- mSetupData = setupData;
- if (result == AccountCheckSettingsFragment.CHECK_SETTINGS_OK) {
- AccountSetupFinal.actionFinal(this, mSetupData);
- mSetupData.setReportAccountAuthenticationError(false);
- finish();
- } else {
- // FLAG: DO I need to do anything else here?
- LogUtils.d(Logging.LOG_TAG, "failure on check setup");
- }
- }
-
- /**
- * Implements AccountCheckSettingsFragment.Callbacks
- * This is overridden only by AccountSetupIncoming
- */
- @Override
- public void onAutoDiscoverComplete(int result, SetupDataFragment setupData) {
- throw new IllegalStateException();
- }
-}
diff --git a/src/com/android/email/activity/setup/SignInFragment.java b/src/com/android/email/activity/setup/SignInFragment.java
deleted file mode 100644
index f0ee15fdb..000000000
--- a/src/com/android/email/activity/setup/SignInFragment.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.email.activity.setup;
-
-import android.app.Fragment;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.EditText;
-import android.widget.Toast;
-
-import com.android.email.R;
-import com.android.email.activity.UiUtilities;
-import com.android.email.activity.setup.SetupDataFragment.SetupDataContainer;
-import com.android.email.service.EmailServiceUtils;
-import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
-import com.android.emailcommon.Logging;
-import com.android.emailcommon.VendorPolicyLoader.OAuthProvider;
-import com.android.emailcommon.VendorPolicyLoader.Provider;
-import com.android.emailcommon.provider.HostAuth;
-import com.android.mail.utils.LogUtils;
-
-import java.util.List;
-
-public class SignInFragment extends Fragment implements OnClickListener {
-
- private View mOAuthGroup;
- private View mOAuthButton;
- private EditText mImapPasswordText;
- private EditText mRegularPasswordText;
- private TextWatcher mValidationTextWatcher;
- private String mEmailAddress;
- private EmailServiceInfo mServiceInfo;
- private String mProviderId;
- private SignInCallback mCallback;
- private Context mContext;
-
- public interface SignInCallback {
- public void onOAuthSignIn(final String providerId, final String accessToken,
- final String refreshToken, final int expiresInSeconds);
-
- public void onValidate();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- final View view = inflater.inflate(R.layout.sign_in_fragment, container, false);
-
- mImapPasswordText = UiUtilities.getView(view, R.id.imap_password);
- mRegularPasswordText = UiUtilities.getView(view, R.id.regular_password);
- mOAuthGroup = UiUtilities.getView(view, R.id.oauth_group);
- mOAuthButton = UiUtilities.getView(view, R.id.sign_in_with_google);
- mOAuthButton.setOnClickListener(this);
-
- // After any text edits, call validateFields() which enables or disables the Next button
- mValidationTextWatcher = new TextWatcher() {
- @Override
- public void afterTextChanged(Editable s) {
- validatePassword();
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) { }
- };
- mImapPasswordText.addTextChangedListener(mValidationTextWatcher);
- mRegularPasswordText.addTextChangedListener(mValidationTextWatcher);
-
- return view;
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- LogUtils.d(Logging.LOG_TAG, "onActivityCreated");
- mContext = getActivity();
- if (mContext instanceof SetupDataContainer) {
- final SetupDataContainer setupContainer = (SetupDataContainer)mContext;
- SetupDataFragment setupData = setupContainer.getSetupData();
- final HostAuth hostAuth = setupData.getAccount().getOrCreateHostAuthRecv(mContext);
- mServiceInfo = EmailServiceUtils.getServiceInfo(mContext,
- hostAuth.mProtocol);
-
- if (mServiceInfo.offerOAuth) {
- mOAuthGroup.setVisibility(View.VISIBLE);
- mRegularPasswordText.setVisibility(View.GONE);
- } else {
- mOAuthGroup.setVisibility(View.GONE);
- mRegularPasswordText.setVisibility(View.VISIBLE);
- }
- }
- validatePassword();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mImapPasswordText.removeTextChangedListener(mValidationTextWatcher);
- mImapPasswordText = null;
- mRegularPasswordText.removeTextChangedListener(mValidationTextWatcher);
- mRegularPasswordText = null;
- }
-
- public void validatePassword() {
- mCallback.onValidate();
- // Warn (but don't prevent) if password has leading/trailing spaces
- AccountSettingsUtils.checkPasswordSpaces(mContext, mImapPasswordText);
- AccountSettingsUtils.checkPasswordSpaces(mContext, mRegularPasswordText);
- }
-
- @Override
- public void onActivityResult(final int requestCode, final int resultCode,
- final Intent data) {
- if (requestCode == OAuthAuthenticationActivity.REQUEST_OAUTH) {
- if (resultCode == OAuthAuthenticationActivity.RESULT_OAUTH_SUCCESS) {
- final String accessToken = data.getStringExtra(
- OAuthAuthenticationActivity.EXTRA_OAUTH_ACCESS_TOKEN);
- final String refreshToken = data.getStringExtra(
- OAuthAuthenticationActivity.EXTRA_OAUTH_REFRESH_TOKEN);
- final int expiresInSeconds = data.getIntExtra(
- OAuthAuthenticationActivity.EXTRA_OAUTH_EXPIRES_IN, 0);
- mCallback.onOAuthSignIn(mProviderId, accessToken, refreshToken, expiresInSeconds);
- } else if (resultCode == OAuthAuthenticationActivity.RESULT_OAUTH_FAILURE
- || resultCode == OAuthAuthenticationActivity.RESULT_OAUTH_USER_CANCELED) {
- LogUtils.i(Logging.LOG_TAG, "Result from oauth %d", resultCode);
- } else {
- LogUtils.wtf(Logging.LOG_TAG, "Unknown result code from OAUTH: %d", resultCode);
- }
- } else {
- LogUtils.e(Logging.LOG_TAG, "Unknown request code for onActivityResult in"
- + " AccountSetupBasics: %d", requestCode);
- }
- }
-
- @Override
- public void onClick(View view) {
- if (view == mOAuthButton) {
- List<OAuthProvider> oauthProviders = AccountSettingsUtils.getAllOAuthProviders(
- mContext);
- // FLAG currently the only oauth provider we support is google.
- // If we ever have more than 1 oauth provider, then we need to implement some sort
- // of picker UI. For now, just always take the first oauth provider.
- if (oauthProviders.size() > 0) {
- mProviderId = oauthProviders.get(0).id;
- final Intent i = new Intent(mContext, OAuthAuthenticationActivity.class);
- i.putExtra(OAuthAuthenticationActivity.EXTRA_EMAIL_ADDRESS, mEmailAddress);
- i.putExtra(OAuthAuthenticationActivity.EXTRA_PROVIDER, mProviderId);
- startActivityForResult(i, OAuthAuthenticationActivity.REQUEST_OAUTH);
- }
- }
- }
-
- public void setEmailAddress(final String emailAddress) {
- mEmailAddress = emailAddress;
- }
-
- public String getEmailAddress() {
- return mEmailAddress;
- }
-
- public EmailServiceInfo setServiceInfo() {
- return mServiceInfo;
- }
- public String getPassword() {
- if (mServiceInfo.offerOAuth) {
- return mImapPasswordText.getText().toString();
- } else {
- return mRegularPasswordText.getText().toString();
- }
- }
-
- public void setSignInCallback(SignInCallback callback) {
- mCallback = callback;
- }
-}
diff --git a/src/com/android/email/mail/Sender.java b/src/com/android/email/mail/Sender.java
index 49f861e89..4e85d70fa 100644
--- a/src/com/android/email/mail/Sender.java
+++ b/src/com/android/email/mail/Sender.java
@@ -117,15 +117,6 @@ public abstract class Sender {
return sender;
}
- /**
- * Get class of SettingActivity for this Sender class.
- * @return Activity class that has class method actionEditOutgoingSettings().
- */
- public Class<? extends android.app.Activity> getSettingActivityClass() {
- // default SettingActivity class
- return com.android.email.activity.setup.AccountSetupOutgoing.class;
- }
-
public abstract void open() throws MessagingException;
public abstract void sendMessage(long messageId) throws MessagingException;
diff --git a/src/com/android/email/mail/store/ImapStore.java b/src/com/android/email/mail/store/ImapStore.java
index 932e462d6..b14e58d34 100644
--- a/src/com/android/email/mail/store/ImapStore.java
+++ b/src/com/android/email/mail/store/ImapStore.java
@@ -114,13 +114,8 @@ public class ImapStore extends Store {
mTransport = new MailTransport(context, "IMAP", recvAuth);
String[] userInfo = recvAuth.getLogin();
- if (userInfo != null) {
- mUsername = userInfo[0];
- mPassword = userInfo[1];
- } else {
- mUsername = null;
- mPassword = null;
- }
+ mUsername = userInfo[0];
+ mPassword = userInfo[1];
final Credential cred = recvAuth.getCredential(context);
mUseOAuth = (cred != null);
mPathPrefix = recvAuth.mDomain;
diff --git a/src/com/android/email/mail/store/Pop3Store.java b/src/com/android/email/mail/store/Pop3Store.java
index c35c02835..cb959fdd0 100644
--- a/src/com/android/email/mail/store/Pop3Store.java
+++ b/src/com/android/email/mail/store/Pop3Store.java
@@ -78,10 +78,8 @@ public class Pop3Store extends Store {
HostAuth recvAuth = account.getOrCreateHostAuthRecv(context);
mTransport = new MailTransport(context, "POP3", recvAuth);
String[] userInfoParts = recvAuth.getLogin();
- if (userInfoParts != null) {
- mUsername = userInfoParts[0];
- mPassword = userInfoParts[1];
- }
+ mUsername = userInfoParts[0];
+ mPassword = userInfoParts[1];
}
/**
diff --git a/src/com/android/email/mail/transport/ExchangeSender.java b/src/com/android/email/mail/transport/ExchangeSender.java
deleted file mode 100644
index 87ba8124b..000000000
--- a/src/com/android/email/mail/transport/ExchangeSender.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.email.mail.transport;
-
-import com.android.email.mail.Sender;
-import com.android.emailcommon.mail.MessagingException;
-import com.android.emailcommon.provider.Account;
-
-import android.content.Context;
-
-/**
- * Our Exchange service does not use the sender/store model. This class exists for exactly one
- * purpose, which is to return "null" for getSettingActivityClass().
- */
-public class ExchangeSender extends Sender {
-
- /**
- * Factory method.
- */
- public static Sender newInstance(Account account, Context context) throws MessagingException {
- return new ExchangeSender(context, account);
- }
-
- private ExchangeSender(Context context, Account account) {
- }
-
- @Override
- public void close() {
- }
-
- @Override
- public void open() {
- }
-
- @Override
- public void sendMessage(long messageId) {
- }
-
- /**
- * Get class of SettingActivity for this Sender class.
- * @return Activity class that has class method actionEditOutgoingSettings(), or null if
- * outgoing settings should not be presented (e.g. they're handled by the incoming settings
- * screen).
- */
- @Override
- public Class<? extends android.app.Activity> getSettingActivityClass() {
- return null;
- }
-
-}
diff --git a/src/com/android/email/mail/transport/SmtpSender.java b/src/com/android/email/mail/transport/SmtpSender.java
index 539ba190e..c57f78cba 100644
--- a/src/com/android/email/mail/transport/SmtpSender.java
+++ b/src/com/android/email/mail/transport/SmtpSender.java
@@ -69,10 +69,8 @@ public class SmtpSender extends Sender {
HostAuth sendAuth = account.getOrCreateHostAuthSend(context);
mTransport = new MailTransport(context, "SMTP", sendAuth);
String[] userInfoParts = sendAuth.getLogin();
- if (userInfoParts != null) {
- mUsername = userInfoParts[0];
- mPassword = userInfoParts[1];
- }
+ mUsername = userInfoParts[0];
+ mPassword = userInfoParts[1];
Credential cred = sendAuth.getCredential(context);
if (cred != null) {
mUseOAuth = true;
diff --git a/src/com/android/email/service/AuthenticatorService.java b/src/com/android/email/service/AuthenticatorService.java
index 2c93c4ba1..36f3222a0 100644
--- a/src/com/android/email/service/AuthenticatorService.java
+++ b/src/com/android/email/service/AuthenticatorService.java
@@ -16,7 +16,7 @@
package com.android.email.service;
-import com.android.email.activity.setup.AccountSetupBasics;
+import com.android.email.activity.setup.AccountSetupFinal;
import com.android.emailcommon.provider.EmailContent;
import android.accounts.AbstractAccountAuthenticator;
@@ -103,7 +103,7 @@ public class AuthenticatorService extends Service {
} else {
Bundle b = new Bundle();
Intent intent =
- AccountSetupBasics.actionGetCreateAccountIntent(AuthenticatorService.this,
+ AccountSetupFinal.actionGetCreateAccountIntent(AuthenticatorService.this,
accountType);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
b.putParcelable(AccountManager.KEY_INTENT, intent);
diff --git a/src/com/android/email/service/EasTestAuthenticatorService.java b/src/com/android/email/service/EasTestAuthenticatorService.java
index cdb213fac..c8d853b93 100644
--- a/src/com/android/email/service/EasTestAuthenticatorService.java
+++ b/src/com/android/email/service/EasTestAuthenticatorService.java
@@ -27,7 +27,7 @@ import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
-import com.android.email.activity.setup.AccountSetupBasics;
+import com.android.email.activity.setup.AccountSetupFinal;
/**
* Anauthenticator service for reconciliation tests; it simply adds the account to AccountManager
@@ -67,7 +67,7 @@ public class EasTestAuthenticatorService extends Service {
} else {
Bundle b = new Bundle();
Intent intent =
- AccountSetupBasics.actionGetCreateAccountIntent(
+ AccountSetupFinal.actionGetCreateAccountIntent(
EasTestAuthenticatorService.this, accountType);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
b.putParcelable(AccountManager.KEY_INTENT, intent);