Remove UI thread disk access from AccountSetupNames

* Remove unneeded DB read in onCreate (add runtime checks for problems)
* Move more exit/commit work to existing AsyncTask
* Add lockout of next & back keys while processing

Bug: 2792509 (probably others)
Change-Id: I0db030a1f1df94ba721510b5112519f260f5c0f9
This commit is contained in:
Andy Stadler 2010-12-25 23:16:10 -08:00
parent 4f6a507d9b
commit 926e7369d1

View File

@ -23,10 +23,10 @@ import com.android.email.activity.ActivityHelper;
import com.android.email.activity.Welcome;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.AccountColumns;
import com.android.email.provider.EmailContent.HostAuth;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
@ -39,16 +39,20 @@ import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
/**
* Final screen of setup process. Collect account nickname and/or username.
*
* TODO: Better processing of account nickname including trimming and prevention of empty string.
*/
public class AccountSetupNames extends AccountSetupActivity implements OnClickListener {
private static final int REQUEST_SECURITY = 0;
private EditText mDescription;
private EditText mName;
private Button mNextButton;
private boolean mNextPressed = false;
private boolean mEasAccount = false;
private CheckAccountStateTask mCheckAccountStateTask;
public static void actionSetNames(Activity fromActivity) {
fromActivity.startActivity(new Intent(fromActivity, AccountSetupNames.class));
}
@ -78,20 +82,15 @@ public class AccountSetupNames extends AccountSetupActivity implements OnClickLi
mName.setKeyListener(TextKeyListener.getInstance(false, Capitalize.WORDS));
Account account = SetupData.getAccount();
// Shouldn't happen, but it could
if (account == null) {
onBackPressed();
return;
throw new IllegalStateException("unexpected null account");
}
// Get the hostAuth for receiving
HostAuth hostAuth = HostAuth.restoreHostAuthWithId(this, account.mHostAuthKeyRecv);
if (hostAuth == null) {
onBackPressed();
return;
if (account.mHostAuthRecv == null) {
throw new IllegalStateException("unexpected null hostauth");
}
// Remember whether we're an EAS account, since it doesn't require the user name field
mEasAccount = hostAuth.mProtocol.equals("eas");
mEasAccount = "eas".equals(account.mHostAuthRecv.mProtocol);
if (mEasAccount) {
mName.setVisibility(View.GONE);
findViewById(R.id.account_name_label).setVisibility(View.GONE);
@ -115,17 +114,6 @@ public class AccountSetupNames extends AccountSetupActivity implements OnClickLi
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mCheckAccountStateTask != null &&
mCheckAccountStateTask.getStatus() != CheckAccountStateTask.Status.FINISHED) {
mCheckAccountStateTask.cancel(true);
mCheckAccountStateTask = null;
}
}
/**
* Implements OnClickListener
*/
@ -133,21 +121,36 @@ public class AccountSetupNames extends AccountSetupActivity implements OnClickLi
public void onClick(View v) {
switch (v.getId()) {
case R.id.next:
onNext();
// Don't allow this more than once (we do some work in an async thread before
// finish()'ing the Activity, which allows this code to potentially be
// executed multiple times.
if (!mNextPressed) {
onNext();
}
mNextPressed = true;
break;
}
}
/**
* TODO: Validator should also trim the name string before checking it.
* Check input fields for legal values and enable/disable next button
*/
private void validateFields() {
boolean newEnabled = mEasAccount || Utility.isTextViewNotEmpty(mName);
mNextButton.setEnabled(newEnabled);
}
/**
* Block the back key if we are currently processing the "next" key"
*/
@Override
public void onBackPressed() {
if (!mNextPressed) {
finishActivity();
}
}
private void finishActivity() {
if (SetupData.getFlowMode() != SetupData.FLOW_MODE_NORMAL) {
AccountSetupBasics.actionAccountCreateFinishedAccountFlow(this);
} else {
@ -164,53 +167,58 @@ public class AccountSetupNames extends AccountSetupActivity implements OnClickLi
}
/**
* After having a chance to input the display names, we normally jump directly to the
* inbox for the new account. However if we're in EAS flow mode (externally-launched
* account creation) we simply "pop" here which should return us to the Accounts activities.
*
* TODO: Validator should also trim the description string before checking it.
* After clicking the next button, we'll start an async task to commit the data
* and other steps to finish the creation of the account.
*/
private void onNext() {
// Update account object from UI
Account account = SetupData.getAccount();
if (Utility.isTextViewNotEmpty(mDescription)) {
account.setDisplayName(mDescription.getText().toString());
}
String name = mName.getText().toString();
account.setSenderName(name);
ContentValues cv = new ContentValues();
cv.put(AccountColumns.DISPLAY_NAME, account.getDisplayName());
cv.put(AccountColumns.SENDER_NAME, name);
account.update(this, cv);
// Update the backup (side copy) of the accounts
AccountBackupRestore.backupAccounts(this);
account.setSenderName(mName.getText().toString());
// Before proceeding, launch an AsyncTask to test the account for any syncing problems,
// and if there's a problem, bring up the UI to update the security level.
mCheckAccountStateTask = new CheckAccountStateTask(account.mId);
mCheckAccountStateTask.execute();
// Launch async task for final commit work
new FinalSetupTask(account).execute();
}
/**
* This async task is launched just before exiting. It's a last chance test, before leaving
* this activity, for the account being in a "hold" state, and gives the user a chance to
* update security, enter a device PIN, etc. for a more seamless account setup experience.
* Final account setup work is handled in this AsyncTask:
* Commit final values to provider
* Trigger account backup
* Check for security hold
*
* When this completes, we return to UI thread for the following steps:
* If security hold, dispatch to AccountSecurity activity
* Otherwise, return to AccountSetupBasics for conclusion.
*
* TODO: If there was *any* indication that security might be required, we could at least
* force the DeviceAdmin activation step, without waiting for the initial sync/handshake
* to fail.
* TODO: If the user doesn't update the security, don't go to the MessageList.
*/
private class CheckAccountStateTask extends AsyncTask<Void, Void, Boolean> {
private class FinalSetupTask extends AsyncTask<Void, Void, Boolean> {
private long mAccountId;
private Account mAccount;
private Context mContext;
public CheckAccountStateTask(long accountId) {
mAccountId = accountId;
public FinalSetupTask(Account account) {
mAccount = account;
mContext = AccountSetupNames.this;
}
@Override
protected Boolean doInBackground(Void... params) {
return Account.isSecurityHold(AccountSetupNames.this, mAccountId);
// Update the account in the database
ContentValues cv = new ContentValues();
cv.put(AccountColumns.DISPLAY_NAME, mAccount.getDisplayName());
cv.put(AccountColumns.SENDER_NAME, mAccount.getSenderName());
mAccount.update(mContext, cv);
// Update the backup (side copy) of the accounts
AccountBackupRestore.backupAccounts(AccountSetupNames.this);
return Account.isSecurityHold(mContext, mAccount.mId);
}
@Override
@ -218,10 +226,10 @@ public class AccountSetupNames extends AccountSetupActivity implements OnClickLi
if (!isCancelled()) {
if (isSecurityHold) {
Intent i = AccountSecurity.actionUpdateSecurityIntent(
AccountSetupNames.this, mAccountId);
AccountSetupNames.this, mAccount.mId);
AccountSetupNames.this.startActivityForResult(i, REQUEST_SECURITY);
} else {
onBackPressed();
finishActivity();
}
}
}
@ -236,7 +244,7 @@ public class AccountSetupNames extends AccountSetupActivity implements OnClickLi
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_SECURITY:
onBackPressed();
finishActivity();
}
super.onActivityResult(requestCode, resultCode, data);
}