diff --git a/emailcommon/src/com/android/emailcommon/service/EmailServiceProxy.java b/emailcommon/src/com/android/emailcommon/service/EmailServiceProxy.java index 36a0d336e..4fc08ee11 100644 --- a/emailcommon/src/com/android/emailcommon/service/EmailServiceProxy.java +++ b/emailcommon/src/com/android/emailcommon/service/EmailServiceProxy.java @@ -18,6 +18,7 @@ package com.android.emailcommon.service; import android.content.Context; import android.content.Intent; +import android.os.AsyncTask; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -30,6 +31,7 @@ import com.android.emailcommon.provider.Policy; import com.android.mail.utils.LogUtils; import java.io.IOException; +import java.util.concurrent.Executor; /** * The EmailServiceProxy class provides a simple interface for the UI to call into the various @@ -258,6 +260,11 @@ public class EmailServiceProxy extends ServiceProxy implements IEmailService { @Override public void deleteExternalAccountPIMData(final String emailAddress) throws RemoteException { setTask(new ProxyTask() { + @Override + public Executor runInExecutor() { + return AsyncTask.THREAD_POOL_EXECUTOR; + } + @Override public void run() throws RemoteException { mService.deleteExternalAccountPIMData(emailAddress); diff --git a/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java b/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java index 3669345c1..8adffc43e 100644 --- a/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java +++ b/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java @@ -31,6 +31,8 @@ import android.os.RemoteException; import com.android.emailcommon.provider.EmailContent; import com.android.mail.utils.LogUtils; +import java.util.concurrent.Executor; + /** * ServiceProxy is a superclass for proxy objects which make a single call to a service. It handles * connecting to the service, running a task supplied by the subclass when the connection is ready, @@ -133,8 +135,8 @@ public abstract class ServiceProxy { "RuntimeException when trying to unbind from service"); } } - mTaskCompleted = true; synchronized(mConnection) { + mTaskCompleted = true; if (DEBUG_PROXY) { LogUtils.v(mTag, "Task " + mName + " completed; disconnecting"); } @@ -142,7 +144,7 @@ public abstract class ServiceProxy { } return null; } - }.execute(); + }.executeOnExecutor(mTask.runInExecutor()); } @Override @@ -154,8 +156,11 @@ public abstract class ServiceProxy { } } - protected interface ProxyTask { - public void run() throws RemoteException; + protected abstract class ProxyTask { + public Executor runInExecutor() { + return AsyncTask.SERIAL_EXECUTOR; + }; + public abstract void run() throws RemoteException; } public ServiceProxy setTimeout(int secs) { @@ -178,6 +183,9 @@ public abstract class ServiceProxy { if (DEBUG_PROXY) { LogUtils.v(mTag, "Bind requested for task " + mName); } + synchronized (mConnection) { + mTaskCompleted = false; + } return mContext.bindService(mIntent, mConnection, Context.BIND_AUTO_CREATE); } @@ -203,7 +211,9 @@ public abstract class ServiceProxy { if (DEBUG_PROXY) { LogUtils.v(mTag, "Waiting for task " + mName + " to complete..."); } - mConnection.wait(mTimeout * 1000L); + if (!mTaskCompleted) { + mConnection.wait(mTimeout * 1000L); + } } catch (InterruptedException e) { // Can be ignored safely } diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml new file mode 100644 index 000000000..0e3594f64 --- /dev/null +++ b/res/values/cm_strings.xml @@ -0,0 +1,24 @@ + + + + + + Delete account + This will delete all the settings and emails of the + account %s. This operation cannot be undone.\n\nContinue? + Deleting account\u2026 + Couldn\'t delete the account + diff --git a/src/com/android/email/activity/setup/AccountSettingsFragment.java b/src/com/android/email/activity/setup/AccountSettingsFragment.java index 30c3d9cb3..49bd8535d 100644 --- a/src/com/android/email/activity/setup/AccountSettingsFragment.java +++ b/src/com/android/email/activity/setup/AccountSettingsFragment.java @@ -16,11 +16,15 @@ package com.android.email.activity.setup; +import android.accounts.AccountManager; import android.app.Activity; +import android.app.AlertDialog; import android.app.LoaderManager; +import android.app.ProgressDialog; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.Loader; import android.content.res.Resources; @@ -28,7 +32,11 @@ import android.database.Cursor; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; +import android.os.AsyncTask; import android.os.Bundle; +import android.os.Handler; +import android.os.Handler.Callback; +import android.os.Message; import android.os.Vibrator; import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; @@ -43,8 +51,12 @@ import android.provider.ContactsContract; import android.provider.Settings; import android.support.annotation.NonNull; import android.text.TextUtils; +import android.util.Log; import android.view.Menu; import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.Window; +import android.widget.Toast; import com.android.email.R; import com.android.email.SecurityPolicy; @@ -63,6 +75,7 @@ import com.android.mail.providers.Folder; import com.android.mail.providers.UIProvider; import com.android.mail.ui.MailAsyncTaskLoader; import com.android.mail.ui.settings.MailAccountPrefsFragment; +import com.android.mail.ui.settings.PublicPreferenceActivity; import com.android.mail.ui.settings.SettingsUtils; import com.android.mail.utils.ContentProviderTask.UpdateTask; import com.android.mail.utils.LogUtils; @@ -118,6 +131,9 @@ public class AccountSettingsFragment extends MailAccountPrefsFragment // Request code to start different activities. private static final int RINGTONE_REQUEST_CODE = 0; + // Message codes + private static final int MSG_DELETE_ACCOUNT = 0; + private EditTextPreference mAccountDescription; private EditTextPreference mAccountName; private EditTextPreference mAccountSignature; @@ -128,6 +144,7 @@ public class AccountSettingsFragment extends MailAccountPrefsFragment private Preference mInboxRingtone; private Context mContext; + private Handler mHandler; private Account mAccount; private com.android.mail.providers.Account mUiAccount; @@ -136,6 +153,18 @@ public class AccountSettingsFragment extends MailAccountPrefsFragment private Ringtone mRingtone; + private MenuItem mDeleteAccountItem; + + private final Callback mHandlerCallback = new Callback() { + @Override + public boolean handleMessage(Message msg) { + if (msg.what == MSG_DELETE_ACCOUNT) { + deleteAccount(); + } + return false; + } + }; + /** * This may be null if the account exists but the inbox has not yet been created in the database * (waiting for initial sync) @@ -176,6 +205,7 @@ public class AccountSettingsFragment extends MailAccountPrefsFragment @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mHandler = new Handler(mHandlerCallback); setHasOptionsMenu(true); @@ -412,9 +442,39 @@ public class AccountSettingsFragment extends MailAccountPrefsFragment @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.clear(); + + // Delete account item + mDeleteAccountItem = menu.add(Menu.NONE, Menu.NONE, 0, R.string.delete_account); + mDeleteAccountItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + inflater.inflate(R.menu.settings_fragment_menu, menu); } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.equals(mDeleteAccountItem)) { + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(mContext); + alertBuilder.setTitle(R.string.delete_account); + String msg = getString(R.string.delete_account_confirmation_msg, mAccountEmail); + alertBuilder.setMessage(msg); + alertBuilder.setCancelable(true); + final DialogInterface.OnClickListener cb = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + mHandler.dispatchMessage(Message.obtain(mHandler, MSG_DELETE_ACCOUNT)); + } + }; + alertBuilder.setPositiveButton(android.R.string.ok, cb); + alertBuilder.setNegativeButton(android.R.string.cancel, null); + alertBuilder.create().show(); + return true; + } + return super.onOptionsItemSelected(item); + } + + + /** * Async task loader to load account in order to view/edit it */ @@ -987,4 +1047,63 @@ public class AccountSettingsFragment extends MailAccountPrefsFragment AccountServerSettingsActivity.getIntentForOutgoing(getActivity(), account); getActivity().startActivity(intent); } + + private void deleteAccount() { + AsyncTask task = new AsyncTask() { + private ProgressDialog mDialog; + + @Override + protected void onPreExecute() { + // Display an alert dialog to advise the user that the operation is in progress + mDialog = new ProgressDialog(mContext); + mDialog.setMessage(mContext.getString(R.string.deleting_account_msg)); + mDialog.setCancelable(false); + mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + mDialog.show(); + } + + @Override + protected void onPostExecute(Boolean result) { + if (!result) { + Toast.makeText(mContext, R.string.delete_account_failed, + Toast.LENGTH_SHORT).show(); + } + mDialog.dismiss(); + } + + @Override + protected Boolean doInBackground(Void... params) { + try { + // Retrieve the necessary information + AccountManager accountManager = (AccountManager)mContext.getSystemService( + Context.ACCOUNT_SERVICE); + android.accounts.Account account = mUiAccount.getAccountManagerAccount(); + + // Remove the email account and its notifications + ContentResolver resolver = mContext.getContentResolver(); + int ret = resolver.delete(mUiAccount.uri, null, null); + if (ret <= 0) { + LogUtils.w(LogUtils.TAG, "Failed to delete account %s", mAccountEmail); + return Boolean.FALSE; + } + NotificationUtils.clearAccountNotifications(mContext, account); + + // And now we remove the system account that holds the email service + accountManager.removeAccount(account, getActivity(), null, null); + + // We deleted the account, so we need to clear the activity stack, so just show + // the settings fragment and clear the activity stack + Intent intent = new Intent(getActivity(), PublicPreferenceActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | + Intent.FLAG_ACTIVITY_CLEAR_TOP); + getActivity().startActivity(intent); + } catch (Exception ex) { + LogUtils.w(LogUtils.TAG, ex, "Failed to delete account %s", mAccountEmail); + return Boolean.FALSE; + } + return Boolean.TRUE; + } + }; + task.execute(); + } }