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.
This commit is contained in:
cketti 2013-09-25 03:46:11 +02:00
parent 62aa1b87d0
commit 677d6c923d
4 changed files with 172 additions and 6 deletions

View File

@ -1149,4 +1149,7 @@ Please submit bug reports, contribute new features and ask questions at
<string name="mark_all_as_read">Mark all as read</string>
<string name="loading_attachment">Loading attachment…</string>
<string name="fetching_attachment_dialog_title_send">Sending message</string>
<string name="fetching_attachment_dialog_title_save">Saving draft</string>
<string name="fetching_attachment_dialog_message">Fetching attachment…</string>
</resources>

View File

@ -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<Attachment> 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<Attachment>() {
@Override
public Loader<Attachment> 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<Attachment> loader) {
onFetchAttachmentFinished();
}
};
@ -2038,14 +2108,48 @@ public class MessageCompose extends K9Activity implements OnClickListener {
}
}
onFetchAttachmentFinished();
getSupportLoaderManager().destroyLoader(loaderId);
}
@Override
public void onLoaderReset(Loader<Attachment> 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) {

View File

@ -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: {

View File

@ -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);
}
}