Remove UI thread access in AccountSetupBasics
* Move getOwnerName to a FutureTask
* Use AsyncTask for call to Utility.findExistingAccount()
Change-Id: I85e35a12cc28b1dda8a0ccd930869316032a3b12
diff --git a/src/com/android/email/activity/setup/AccountSetupBasics.java b/src/com/android/email/activity/setup/AccountSetupBasics.java
index ca604b3..ea20301 100644
--- a/src/com/android/email/activity/setup/AccountSetupBasics.java
+++ b/src/com/android/email/activity/setup/AccountSetupBasics.java
@@ -55,6 +55,9 @@
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
/**
* Prompts the user for the email address and password. Also prompts for "Use this account as
@@ -113,6 +116,7 @@
private Provider mProvider;
private Button mManualButton;
private Button mNextButton;
+ private boolean mNextButtonInhibit;
// Used when this Activity is called as part of account authentification flow,
// which requires to do extra work before and after the account creation.
@@ -120,6 +124,9 @@
private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null;
private Bundle mResultBundle = null;
+ // FutureTask to look up the owner
+ FutureTask<String> mOwnerLookupTask;
+
public static void actionNewAccount(Activity fromActivity) {
SetupData.init(SetupData.FLOW_MODE_NORMAL);
fromActivity.startActivity(new Intent(fromActivity, AccountSetupBasics.class));
@@ -230,6 +237,8 @@
mNextButton.setOnClickListener(this);
// Force disabled until validator notifies otherwise
onEnableProceedButtons(false);
+ // Lightweight debounce while Async tasks underway
+ mNextButtonInhibit = false;
mAccountAuthenticatorResponse =
getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
@@ -278,6 +287,11 @@
if (savedInstanceState != null && savedInstanceState.containsKey(STATE_KEY_PROVIDER)) {
mProvider = (Provider) savedInstanceState.getSerializable(STATE_KEY_PROVIDER);
}
+
+ // Launch a worker to look up the owner name. It should be ready well in advance of
+ // the time the user clicks next or manual.
+ mOwnerLookupTask = new FutureTask<String>(mOwnerLookupCallable);
+ Utility.runAsync(mOwnerLookupTask);
}
@Override
@@ -310,6 +324,10 @@
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:
@@ -345,22 +363,37 @@
}
/**
- * TODO this should also be in AsyncTask
- * TODO figure out another way to get the owner name
+ * Return an existing username if found, or null. This is the result of the Callable (below).
*/
private String getOwnerName() {
- String name = null;
- long defaultId = Account.getDefaultAccountId(this);
- if (defaultId != -1) {
- Account account = Account.restoreAccountWithId(this, defaultId);
- if (account != null) {
- name = account.getSenderName();
- }
+ String result = null;
+ try {
+ result = mOwnerLookupTask.get();
+ } catch (InterruptedException e) {
+ } catch (ExecutionException e) {
}
- return name;
+ return result;
}
/**
+ * Callable that returns the username (based on other accounts) or null.
+ */
+ private Callable<String> mOwnerLookupCallable = new Callable<String>() {
+ public String call() {
+ Context context = AccountSetupBasics.this;
+ String name = null;
+ long defaultId = Account.getDefaultAccountId(context);
+ if (defaultId != -1) {
+ Account account = Account.restoreAccountWithId(context, defaultId);
+ if (account != null) {
+ name = account.getSenderName();
+ }
+ }
+ return name;
+ }
+ };
+
+ /**
* Finish the auto setup process, in some cases after showing a warning dialog.
*/
private void finishAutoSetup() {
@@ -371,8 +404,8 @@
String domain = emailParts[1];
URI incomingUri = null;
URI outgoingUri = null;
+ String incomingUsername = mProvider.incomingUsernameTemplate;
try {
- String incomingUsername = mProvider.incomingUsernameTemplate;
incomingUsername = incomingUsername.replaceAll("\\$email", email);
incomingUsername = incomingUsername.replaceAll("\\$user", user);
incomingUsername = incomingUsername.replaceAll("\\$domain", domain);
@@ -392,17 +425,6 @@
+ password, outgoingUriTemplate.getHost(), outgoingUriTemplate.getPort(),
outgoingUriTemplate.getPath(), null, null);
- // Stop here if the login credentials duplicate an existing account
- // TODO this shouldn't be in UI thread
- Account account = Utility.findExistingAccount(this, -1,
- incomingUri.getHost(), incomingUsername);
- if (account != null) {
- DuplicateAccountDialogFragment dialogFragment =
- DuplicateAccountDialogFragment.newInstance(account.mDisplayName);
- dialogFragment.show(getFragmentManager(), DuplicateAccountDialogFragment.TAG);
- return;
- }
-
} catch (URISyntaxException use) {
/*
* If there is some problem with the URI we give up and go on to
@@ -414,22 +436,62 @@
return;
}
+ // Populate the setup data, assuming that the duplicate account check will succeed
populateSetupData(getOwnerName(), email, mDefaultView.isChecked(),
incomingUri.toString(), outgoingUri.toString());
- /**
- * Start the account checker fragment
- * TODO how to link directly from activity<-->fragment
- */
- AccountCheckSettingsFragment checkerFragment = AccountCheckSettingsFragment.newInstance(
- SetupData.CHECK_INCOMING | SetupData.CHECK_OUTGOING, null);
- FragmentTransaction transaction = getFragmentManager().openTransaction();
- transaction.add(checkerFragment, AccountCheckSettingsFragment.TAG);
- transaction.addToBackStack("back");
- transaction.commit();
+ // Stop here if the login credentials duplicate an existing account
+ // Launch an Async task to do the work
+ new DuplicateCheckTask(this, incomingUri.getHost(), incomingUsername).execute();
}
/**
+ * 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, Account> {
+ private final Context mContext;
+ private final String mCheckHost;
+ private final String mCheckLogin;
+
+ public DuplicateCheckTask(Context context, String checkHost, String checkLogin) {
+ mContext = context;
+ mCheckHost = checkHost;
+ mCheckLogin = checkLogin;
+ // Prevent additional clicks on the next button during Async lookup
+ mNextButtonInhibit = true;
+ }
+
+ @Override
+ protected Account doInBackground(Void... params) {
+ EmailContent.Account account = Utility.findExistingAccount(mContext, -1,
+ mCheckHost, mCheckLogin);
+ return account;
+ }
+
+ @Override
+ protected void onPostExecute(Account duplicateAccount) {
+ mNextButtonInhibit = false;
+ // Show duplicate account warning, or proceed
+ if (duplicateAccount != null) {
+ DuplicateAccountDialogFragment dialogFragment =
+ DuplicateAccountDialogFragment.newInstance(duplicateAccount.mDisplayName);
+ dialogFragment.show(getFragmentManager(), DuplicateAccountDialogFragment.TAG);
+ return;
+ } else {
+ AccountCheckSettingsFragment checkerFragment =
+ AccountCheckSettingsFragment.newInstance(
+ SetupData.CHECK_INCOMING | SetupData.CHECK_OUTGOING, null);
+ FragmentTransaction transaction = getFragmentManager().openTransaction();
+ transaction.add(checkerFragment, AccountCheckSettingsFragment.TAG);
+ transaction.addToBackStack("back");
+ transaction.commit();
+ }
+ }
+ }
+
+
+ /**
* When "next" button is clicked
*/
private void onNext() {