From 677d6c923d7dc7aa00c35a32fe0067a71611f70d Mon Sep 17 00:00:00 2001 From: cketti Date: Wed, 25 Sep 2013 03:46:11 +0200 Subject: [PATCH] Don't stop the activity before attachments have been fetched Display a progress dialog when the user tries to send the message or save a draft and the attachments haven't been fetched completely. --- res/values/strings.xml | 3 + src/com/fsck/k9/activity/MessageCompose.java | 147 +++++++++++++++++- .../fsck/k9/fragment/MessageViewFragment.java | 4 +- .../k9/fragment/ProgressDialogFragment.java | 24 ++- 4 files changed, 172 insertions(+), 6 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index fd4bd3f0f..638c6507c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1149,4 +1149,7 @@ Please submit bug reports, contribute new features and ask questions at Mark all as read Loading attachment… + Sending message + Saving draft + Fetching attachment… diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index 999bb6947..ab999c0ec 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -61,6 +61,7 @@ import com.fsck.k9.controller.MessagingController; import com.fsck.k9.controller.MessagingListener; import com.fsck.k9.crypto.CryptoProvider; import com.fsck.k9.crypto.PgpData; +import com.fsck.k9.fragment.ProgressDialogFragment; import com.fsck.k9.helper.ContactItem; import com.fsck.k9.helper.Contacts; import com.fsck.k9.helper.HtmlConverter; @@ -94,7 +95,6 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -103,7 +103,9 @@ import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class MessageCompose extends K9Activity implements OnClickListener { +public class MessageCompose extends K9Activity implements OnClickListener, + ProgressDialogFragment.CancelListener { + private static final int DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE = 1; private static final int DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED = 2; private static final int DIALOG_CONTINUE_WITHOUT_PUBLIC_KEY = 3; @@ -147,14 +149,19 @@ public class MessageCompose extends K9Activity implements OnClickListener { "com.fsck.k9.activity.MessageCompose.forcePlainText"; private static final String STATE_KEY_QUOTED_TEXT_FORMAT = "com.fsck.k9.activity.MessageCompose.quotedTextFormat"; + private static final String STATE_KEY_NUM_ATTACHMENTS_LOADING = "numAttachmentsLoading"; + private static final String STATE_KEY_WAITING_FOR_ATTACHMENTS = "waitingForAttachments"; private static final String LOADER_ARG_ATTACHMENT = "attachment"; + private static final String FRAGMENT_WAITING_FOR_ATTACHMENT = "waitingForAttachment"; + private static final int MSG_PROGRESS_ON = 1; private static final int MSG_PROGRESS_OFF = 2; private static final int MSG_SKIPPED_ATTACHMENTS = 3; private static final int MSG_SAVED_DRAFT = 4; private static final int MSG_DISCARDED_DRAFT = 5; + private static final int MSG_PERFORM_STALLED_ACTION = 6; private static final int ACTIVITY_REQUEST_PICK_ATTACHMENT = 1; private static final int CONTACT_PICKER_TO = 4; @@ -326,6 +333,23 @@ public class MessageCompose extends K9Activity implements OnClickListener { */ private long mDraftId = INVALID_DRAFT_ID; + /** + * Number of attachments currently being fetched. + */ + private int mNumAttachmentsLoading = 0; + + private enum WaitingAction { + NONE, + SEND, + SAVE + } + + /** + * Specifies what action to perform once attachments have been fetched. + */ + private WaitingAction mWaitingForAttachments = WaitingAction.NONE; + + private Handler mHandler = new Handler() { @Override public void handleMessage(android.os.Message msg) { @@ -354,6 +378,9 @@ public class MessageCompose extends K9Activity implements OnClickListener { getString(R.string.message_discarded_toast), Toast.LENGTH_LONG).show(); break; + case MSG_PERFORM_STALLED_ACTION: + performStalledAction(); + break; default: super.handleMessage(msg); break; @@ -1080,6 +1107,8 @@ public class MessageCompose extends K9Activity implements OnClickListener { attachments.add(attachment); } + outState.putInt(STATE_KEY_NUM_ATTACHMENTS_LOADING, mNumAttachmentsLoading); + outState.putString(STATE_KEY_WAITING_FOR_ATTACHMENTS, mWaitingForAttachments.name()); outState.putParcelableArrayList(STATE_KEY_ATTACHMENTS, attachments); outState.putBoolean(STATE_KEY_CC_SHOWN, mCcWrapper.getVisibility() == View.VISIBLE); outState.putBoolean(STATE_KEY_BCC_SHOWN, mBccWrapper.getVisibility() == View.VISIBLE); @@ -1105,6 +1134,16 @@ public class MessageCompose extends K9Activity implements OnClickListener { mAttachments.removeAllViews(); mMaxLoaderId = 0; + mNumAttachmentsLoading = savedInstanceState.getInt(STATE_KEY_NUM_ATTACHMENTS_LOADING); + mWaitingForAttachments = WaitingAction.NONE; + try { + String waitingFor = savedInstanceState.getString(STATE_KEY_WAITING_FOR_ATTACHMENTS); + mWaitingForAttachments = WaitingAction.valueOf(waitingFor); + } catch (Exception e) { + Log.w(K9.LOG_TAG, "Couldn't read value \" + STATE_KEY_WAITING_FOR_ATTACHMENTS +" + + "\" from saved instance state", e); + } + ArrayList attachments = savedInstanceState.getParcelableArrayList(STATE_KEY_ATTACHMENTS); for (Attachment attachment : attachments) { addAttachmentView(attachment); @@ -1784,6 +1823,20 @@ public class MessageCompose extends K9Activity implements OnClickListener { Toast.makeText(this, getString(R.string.message_compose_error_no_recipients), Toast.LENGTH_LONG).show(); return; } + + if (mWaitingForAttachments != WaitingAction.NONE) { + return; + } + + if (mNumAttachmentsLoading > 0) { + mWaitingForAttachments = WaitingAction.SEND; + showWaitingForAttachmentDialog(); + } else { + performSend(); + } + } + + private void performSend() { final CryptoProvider crypto = mAccount.getCryptoProvider(); if (mEncryptCheckbox.isChecked() && !mPgpData.hasEncryptionKeys()) { // key selection before encryption @@ -1847,6 +1900,19 @@ public class MessageCompose extends K9Activity implements OnClickListener { } private void onSave() { + if (mWaitingForAttachments != WaitingAction.NONE) { + return; + } + + if (mNumAttachmentsLoading > 0) { + mWaitingForAttachments = WaitingAction.SAVE; + showWaitingForAttachmentDialog(); + } else { + performSend(); + } + } + + private void performSave() { saveIfNeeded(); finish(); } @@ -1987,6 +2053,7 @@ public class MessageCompose extends K9Activity implements OnClickListener { new LoaderManager.LoaderCallbacks() { @Override public Loader onCreateLoader(int id, Bundle args) { + onFetchAttachmentStarted(); Attachment attachment = args.getParcelable(LOADER_ARG_ATTACHMENT); return new AttachmentInfoLoader(MessageCompose.this, attachment); } @@ -2004,6 +2071,8 @@ public class MessageCompose extends K9Activity implements OnClickListener { attachment.loaderId = ++mMaxLoaderId; initAttachmentContentLoader(attachment); + } else { + onFetchAttachmentFinished(); } getSupportLoaderManager().destroyLoader(loaderId); @@ -2011,6 +2080,7 @@ public class MessageCompose extends K9Activity implements OnClickListener { @Override public void onLoaderReset(Loader loader) { + onFetchAttachmentFinished(); } }; @@ -2038,14 +2108,48 @@ public class MessageCompose extends K9Activity implements OnClickListener { } } + onFetchAttachmentFinished(); + getSupportLoaderManager().destroyLoader(loaderId); } @Override public void onLoaderReset(Loader loader) { + onFetchAttachmentFinished(); } }; + private void onFetchAttachmentStarted() { + mNumAttachmentsLoading += 1; + } + + private void onFetchAttachmentFinished() { + // We're not allowed to perform fragment transactions when called from onLoadFinished(). + // So we use the Handler to call performStalledAction(). + mHandler.sendEmptyMessage(MSG_PERFORM_STALLED_ACTION); + } + + private void performStalledAction() { + mNumAttachmentsLoading -= 1; + + WaitingAction waitingFor = mWaitingForAttachments; + mWaitingForAttachments = WaitingAction.NONE; + + if (waitingFor != WaitingAction.NONE) { + dismissWaitingForAttachmentDialog(); + } + + switch (waitingFor) { + case SEND: { + performSend(); + break; + } + case SAVE: { + performSave(); + break; + } + } + } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { @@ -2380,6 +2484,45 @@ public class MessageCompose extends K9Activity implements OnClickListener { } } + private void showWaitingForAttachmentDialog() { + String title; + + switch (mWaitingForAttachments) { + case SEND: { + title = getString(R.string.fetching_attachment_dialog_title_send); + break; + } + case SAVE: { + title = getString(R.string.fetching_attachment_dialog_title_save); + break; + } + default: { + return; + } + } + + ProgressDialogFragment fragment = ProgressDialogFragment.newInstance(title, + getString(R.string.fetching_attachment_dialog_message)); + fragment.show(getSupportFragmentManager(), FRAGMENT_WAITING_FOR_ATTACHMENT); + } + + public void onCancel(ProgressDialogFragment fragment) { + attachmentProgressDialogCancelled(); + } + + void attachmentProgressDialogCancelled() { + mWaitingForAttachments = WaitingAction.NONE; + } + + private void dismissWaitingForAttachmentDialog() { + ProgressDialogFragment fragment = (ProgressDialogFragment) + getSupportFragmentManager().findFragmentByTag(FRAGMENT_WAITING_FOR_ATTACHMENT); + + if (fragment != null) { + fragment.dismiss(); + } + } + @Override public Dialog onCreateDialog(int id) { switch (id) { diff --git a/src/com/fsck/k9/fragment/MessageViewFragment.java b/src/com/fsck/k9/fragment/MessageViewFragment.java index d2bd16d89..711fcb93d 100644 --- a/src/com/fsck/k9/fragment/MessageViewFragment.java +++ b/src/com/fsck/k9/fragment/MessageViewFragment.java @@ -752,8 +752,8 @@ public class MessageViewFragment extends SherlockFragment implements OnClickList break; } case R.id.dialog_attachment_progress: { - String title = getString(R.string.dialog_attachment_progress_title); - fragment = ProgressDialogFragment.newInstance(title); + String message = getString(R.string.dialog_attachment_progress_title); + fragment = ProgressDialogFragment.newInstance(null, message); break; } default: { diff --git a/src/com/fsck/k9/fragment/ProgressDialogFragment.java b/src/com/fsck/k9/fragment/ProgressDialogFragment.java index 671758a86..467bbcc83 100644 --- a/src/com/fsck/k9/fragment/ProgressDialogFragment.java +++ b/src/com/fsck/k9/fragment/ProgressDialogFragment.java @@ -2,19 +2,22 @@ package com.fsck.k9.fragment; import android.app.Dialog; import android.app.ProgressDialog; +import android.content.DialogInterface; import android.os.Bundle; import com.actionbarsherlock.app.SherlockDialogFragment; public class ProgressDialogFragment extends SherlockDialogFragment { - private static final String ARG_TITLE = "title"; + protected static final String ARG_TITLE = "title"; + protected static final String ARG_MESSAGE = "message"; - public static ProgressDialogFragment newInstance(String title) { + public static ProgressDialogFragment newInstance(String title, String message) { ProgressDialogFragment fragment = new ProgressDialogFragment(); Bundle args = new Bundle(); args.putString(ARG_TITLE, title); + args.putString(ARG_MESSAGE, message); fragment.setArguments(args); return fragment; @@ -25,11 +28,28 @@ public class ProgressDialogFragment extends SherlockDialogFragment { public Dialog onCreateDialog(Bundle savedInstanceState) { Bundle args = getArguments(); String title = args.getString(ARG_TITLE); + String message = args.getString(ARG_MESSAGE); ProgressDialog dialog = new ProgressDialog(getActivity()); dialog.setIndeterminate(true); dialog.setTitle(title); + dialog.setMessage(message); return dialog; } + + @Override + public void onCancel(DialogInterface dialog) { + CancelListener listener = (CancelListener) getActivity(); + if (listener != null && listener instanceof CancelListener) { + listener.onCancel(this); + } + + super.onCancel(dialog); + } + + + public interface CancelListener { + void onCancel(ProgressDialogFragment fragment); + } }