diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/AttachmentProgressCallback.java b/k9mail-library/src/main/java/com/fsck/k9/mail/AttachmentProgressCallback.java new file mode 100644 index 000000000..e85f6e448 --- /dev/null +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/AttachmentProgressCallback.java @@ -0,0 +1,5 @@ +package com.fsck.k9.mail; + +public interface AttachmentProgressCallback { + void onUpdate(int progress); +} diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/Folder.java b/k9mail-library/src/main/java/com/fsck/k9/mail/Folder.java index 6545a7547..2edf6fe81 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/Folder.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/Folder.java @@ -128,7 +128,8 @@ public abstract class Folder { MessageRetrievalListener listener) throws MessagingException; public void fetchPart(Message message, Part part, - MessageRetrievalListener listener) throws MessagingException { + MessageRetrievalListener listener, + AttachmentProgressCallback progressCallback) throws MessagingException { // This is causing trouble. Disabled for now. See issue 1733 //throw new RuntimeException("fetchPart() not implemented."); diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeMessage.java b/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeMessage.java index 557927734..98a78e183 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeMessage.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeMessage.java @@ -576,7 +576,7 @@ public class MimeMessage extends Message { @Override public void body(BodyDescriptor bd, InputStream in) throws IOException, MimeException { expect(Part.class); - Body body = MimeUtility.createBody(in, bd.getTransferEncoding(), bd.getMimeType()); + Body body = MimeUtility.createBody(in, bd.getTransferEncoding(), bd.getMimeType(), null); ((Part)stack.peek()).setBody(body); } diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeUtility.java b/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeUtility.java index 13ee30a27..98431c945 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeUtility.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeUtility.java @@ -6,17 +6,22 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Locale; +import java.util.Timer; +import java.util.TimerTask; import java.util.regex.Pattern; import android.support.annotation.NonNull; +import com.fsck.k9.mail.AttachmentProgressCallback; import com.fsck.k9.mail.Body; import com.fsck.k9.mail.BodyPart; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Multipart; import com.fsck.k9.mail.Part; + import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.CountingOutputStream; import org.apache.james.mime4j.codec.Base64InputStream; import org.apache.james.mime4j.codec.QuotedPrintableInputStream; import org.apache.james.mime4j.util.MimeUtil; @@ -985,14 +990,14 @@ public class MimeUtility { return isSameMimeType(mimeType, DEFAULT_ATTACHMENT_MIME_TYPE); } - public static Body createBody(InputStream in, String contentTransferEncoding, String contentType) + public static Body createBody(InputStream in, String contentTransferEncoding, String contentType, final AttachmentProgressCallback progressCallback) throws IOException { if (contentTransferEncoding != null) { contentTransferEncoding = MimeUtility.getHeaderParameter(contentTransferEncoding, null); } - BinaryTempFileBody tempBody; + final BinaryTempFileBody tempBody; if (MimeUtil.isMessage(contentType)) { tempBody = new BinaryTempFileMessageBody(contentTransferEncoding); } else { @@ -1000,9 +1005,23 @@ public class MimeUtility { } OutputStream out = tempBody.getOutputStream(); + final CountingOutputStream countingOutputStream = new CountingOutputStream(out); + Timer timer = null; try { - IOUtils.copy(in, out); + if (progressCallback != null) { + timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + progressCallback.onUpdate((int) countingOutputStream.getCount()); + } + }, 0, 50); + } + IOUtils.copy(in, countingOutputStream); } finally { + if (timer != null) { + timer.cancel(); + } out.close(); } diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/FetchPartCallback.java b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/FetchPartCallback.java index 2ecd2ef50..abed997b2 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/FetchPartCallback.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/FetchPartCallback.java @@ -3,6 +3,7 @@ package com.fsck.k9.mail.store.imap; import java.io.IOException; +import com.fsck.k9.mail.AttachmentProgressCallback; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.filter.FixedLengthInputStream; import com.fsck.k9.mail.internet.MimeHeader; @@ -11,11 +12,17 @@ import com.fsck.k9.mail.internet.MimeUtility; class FetchPartCallback implements ImapResponseCallback { private Part mPart; + private AttachmentProgressCallback attachmentProgressCallback; FetchPartCallback(Part part) { mPart = part; } + FetchPartCallback(Part part, AttachmentProgressCallback attachmentProgressCallback) { + mPart = part; + this.attachmentProgressCallback = attachmentProgressCallback; + } + @Override public Object foundLiteral(ImapResponse response, FixedLengthInputStream literal) throws IOException { if (response.getTag() == null && @@ -27,7 +34,11 @@ class FetchPartCallback implements ImapResponseCallback { String contentType = mPart .getHeader(MimeHeader.HEADER_CONTENT_TYPE)[0]; - return MimeUtility.createBody(literal, contentTransferEncoding, contentType); + if (attachmentProgressCallback != null) { + return MimeUtility.createBody(literal, contentTransferEncoding, contentType, attachmentProgressCallback); + } + + return MimeUtility.createBody(literal, contentTransferEncoding, contentType, null); } return null; } diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapFolder.java b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapFolder.java index 44bbb8347..5002db525 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapFolder.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapFolder.java @@ -18,6 +18,7 @@ import java.util.concurrent.ConcurrentHashMap; import android.text.TextUtils; +import com.fsck.k9.mail.AttachmentProgressCallback; import com.fsck.k9.mail.Body; import com.fsck.k9.mail.FetchProfile; import com.fsck.k9.mail.Flag; @@ -779,7 +780,7 @@ class ImapFolder extends Folder { } @Override - public void fetchPart(Message message, Part part, MessageRetrievalListener listener) + public void fetchPart(Message message, Part part, MessageRetrievalListener listener, AttachmentProgressCallback progressCallback) throws MessagingException { checkOpen(); @@ -800,7 +801,7 @@ class ImapFolder extends Folder { ImapResponse response; int messageNumber = 0; - ImapResponseCallback callback = new FetchPartCallback(part); + ImapResponseCallback callback = new FetchPartCallback(part, progressCallback); do { response = connection.readResponse(callback); @@ -838,7 +839,7 @@ class ImapFolder extends Folder { part.getHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0]; String contentType = part.getHeader(MimeHeader.HEADER_CONTENT_TYPE)[0]; MimeMessageHelper.setBody(part, MimeUtility.createBody(bodyStream, contentTransferEncoding, - contentType)); + contentType, progressCallback)); } else { // This shouldn't happen throw new MessagingException("Got FETCH response with bogus parameters"); diff --git a/k9mail-library/src/test/java/com/fsck/k9/mail/store/imap/ImapFolderTest.java b/k9mail-library/src/test/java/com/fsck/k9/mail/store/imap/ImapFolderTest.java index 25bfcdabd..ce10c829a 100644 --- a/k9mail-library/src/test/java/com/fsck/k9/mail/store/imap/ImapFolderTest.java +++ b/k9mail-library/src/test/java/com/fsck/k9/mail/store/imap/ImapFolderTest.java @@ -934,7 +934,7 @@ public class ImapFolderTest { Part part = createPart("TEXT"); when(imapConnection.readResponse(any(ImapResponseCallback.class))).thenReturn(createImapResponse("x OK")); - folder.fetchPart(message, part, null); + folder.fetchPart(message, part, null, null); verify(imapConnection).sendCommand("UID FETCH 1 (UID BODY.PEEK[TEXT]<0.4096>)", false); } @@ -948,7 +948,7 @@ public class ImapFolderTest { Part part = createPart("1.1"); when(imapConnection.readResponse(any(ImapResponseCallback.class))).thenReturn(createImapResponse("x OK")); - folder.fetchPart(message, part, null); + folder.fetchPart(message, part, null, null); verify(imapConnection).sendCommand("UID FETCH 1 (UID BODY.PEEK[1.1])", false); } @@ -962,7 +962,7 @@ public class ImapFolderTest { Part part = createPlainTextPart("1.1"); setupSingleFetchResponseToCallback(); - folder.fetchPart(message, part, null); + folder.fetchPart(message, part, null, null); ArgumentCaptor bodyArgumentCaptor = ArgumentCaptor.forClass(Body.class); verify(part).setBody(bodyArgumentCaptor.capture()); diff --git a/k9mail/src/main/java/com/fsck/k9/activity/MessageCompose.java b/k9mail/src/main/java/com/fsck/k9/activity/MessageCompose.java index 5f254d64b..822a3290c 100644 --- a/k9mail/src/main/java/com/fsck/k9/activity/MessageCompose.java +++ b/k9mail/src/main/java/com/fsck/k9/activity/MessageCompose.java @@ -69,8 +69,11 @@ import com.fsck.k9.activity.misc.Attachment; import com.fsck.k9.controller.MessagingController; import com.fsck.k9.controller.MessagingListener; import com.fsck.k9.controller.SimpleMessagingListener; +import com.fsck.k9.fragment.AttachmentDownloadDialogFragment; import com.fsck.k9.fragment.ProgressDialogFragment; import com.fsck.k9.fragment.ProgressDialogFragment.CancelListener; +import com.fsck.k9.fragment.AttachmentDownloadDialogFragment; +import com.fsck.k9.fragment.AttachmentDownloadDialogFragment.AttachmentDownloadCancelListener; import com.fsck.k9.helper.Contacts; import com.fsck.k9.helper.IdentityHelper; import com.fsck.k9.helper.MailTo; @@ -101,7 +104,7 @@ import com.fsck.k9.ui.compose.QuotedMessagePresenter; @SuppressWarnings("deprecation") // TODO get rid of activity dialogs and indeterminate progress bars public class MessageCompose extends K9Activity implements OnClickListener, - CancelListener, OnFocusChangeListener, OnCryptoModeChangedListener, + CancelListener, AttachmentDownloadCancelListener, OnFocusChangeListener, OnCryptoModeChangedListener, OnOpenPgpInlineChangeListener, OnOpenPgpSignOnlyChangeListener, MessageBuilder.Callback, AttachmentPresenter.AttachmentsChangedListener, RecipientPresenter.RecipientsChangedListener { @@ -1053,6 +1056,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, return false; } + @Override + public void onProgressCancel(AttachmentDownloadDialogFragment fragment) { + attachmentPresenter.attachmentProgressDialogCancelled(); + } public void onProgressCancel(ProgressDialogFragment fragment) { attachmentPresenter.attachmentProgressDialogCancelled(); diff --git a/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java b/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java index 01facaee7..63ef13053 100644 --- a/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java +++ b/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java @@ -18,6 +18,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; @@ -63,8 +65,10 @@ import com.fsck.k9.controller.MessagingControllerCommands.PendingExpunge; import com.fsck.k9.controller.MessagingControllerCommands.PendingMarkAllAsRead; import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveOrCopy; import com.fsck.k9.controller.MessagingControllerCommands.PendingSetFlag; +import com.fsck.k9.fragment.AttachmentDownloadDialogFragment; import com.fsck.k9.helper.Contacts; import com.fsck.k9.mail.Address; +import com.fsck.k9.mail.AttachmentProgressCallback; import com.fsck.k9.mail.AuthenticationFailedException; import com.fsck.k9.mail.CertificateValidationException; import com.fsck.k9.mail.FetchProfile; @@ -150,6 +154,8 @@ public class MessagingController { private MessagingListener checkMailListener = null; private volatile boolean stopped = false; + private MessagingListener attachmentDownloadProgressListener = null; + public static synchronized MessagingController getInstance(Context context) { if (inst == null) { @@ -259,6 +265,10 @@ public class MessagingController { throw new Error(e); } + public void addDownloadProgressListener(MessagingListener messagingListener){ + attachmentDownloadProgressListener = messagingListener; + } + public void addListener(MessagingListener listener) { listeners.add(listener); refreshListener(listener); @@ -1483,7 +1493,7 @@ public class MessagingController { * Now download the parts we're interested in storing. */ for (Part part : viewables) { - remoteFolder.fetchPart(message, part, null); + remoteFolder.fetchPart(message, part, null, null); } // Store the updated message locally localFolder.appendMessages(Collections.singletonList(message)); @@ -2551,8 +2561,15 @@ public class MessagingController { remoteFolder = remoteStore.getFolder(folderName); remoteFolder.open(Folder.OPEN_MODE_RW); + AttachmentProgressCallback attachmentProgressCallback = new AttachmentProgressCallback() { + @Override + public void onUpdate(int progress) { + attachmentDownloadProgressListener.updateProgress(progress); + } + }; + Message remoteMessage = remoteFolder.getMessage(message.getUid()); - remoteFolder.fetchPart(remoteMessage, part, null); + remoteFolder.fetchPart(remoteMessage, part, null, attachmentProgressCallback); localFolder.addPartToMessage(message, part); diff --git a/k9mail/src/main/java/com/fsck/k9/controller/MessagingListener.java b/k9mail/src/main/java/com/fsck/k9/controller/MessagingListener.java index 8cc15e70a..6a3e3f97a 100644 --- a/k9mail/src/main/java/com/fsck/k9/controller/MessagingListener.java +++ b/k9mail/src/main/java/com/fsck/k9/controller/MessagingListener.java @@ -73,4 +73,6 @@ public interface MessagingListener { void remoteSearchFailed(String folder, String err); void enableProgressIndicator(boolean enable); + + void updateProgress(int progress); } diff --git a/k9mail/src/main/java/com/fsck/k9/controller/SimpleMessagingListener.java b/k9mail/src/main/java/com/fsck/k9/controller/SimpleMessagingListener.java index 785191b84..3998981ed 100644 --- a/k9mail/src/main/java/com/fsck/k9/controller/SimpleMessagingListener.java +++ b/k9mail/src/main/java/com/fsck/k9/controller/SimpleMessagingListener.java @@ -181,4 +181,9 @@ public abstract class SimpleMessagingListener implements MessagingListener { @Override public void enableProgressIndicator(boolean enable) { } + + @Override + public void updateProgress(int progress) { + + } } diff --git a/k9mail/src/main/java/com/fsck/k9/fragment/AttachmentDownloadDialogFragment.java b/k9mail/src/main/java/com/fsck/k9/fragment/AttachmentDownloadDialogFragment.java new file mode 100644 index 000000000..ce5426f1e --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/fragment/AttachmentDownloadDialogFragment.java @@ -0,0 +1,83 @@ +package com.fsck.k9.fragment; + +import android.app.Activity; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.os.Bundle; + +import com.fsck.k9.controller.MessagingController; +import com.fsck.k9.controller.MessagingListener; +import com.fsck.k9.controller.SimpleMessagingListener; + +public class AttachmentDownloadDialogFragment extends DialogFragment { + + ProgressDialog dialog; + + int progress = 0; + + MessagingListener messagingListener; + + protected static final String ARG_SIZE = "size"; + protected static final String ARG_MESSAGE = "message"; + + public static AttachmentDownloadDialogFragment newInstance(int size, String message) { + AttachmentDownloadDialogFragment attachmentDownloadDialogFragment = new AttachmentDownloadDialogFragment(); + + Bundle args = new Bundle(); + args.putInt(ARG_SIZE, size); + args.putString(ARG_MESSAGE, message); + attachmentDownloadDialogFragment.setArguments(args); + + return attachmentDownloadDialogFragment; + } + + public void updateDownloadProgress(int newProgress) { + progress = newProgress; + dialog.setProgress(newProgress); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Bundle args = getArguments(); + int size = args.getInt(ARG_SIZE); + String message = args.getString(ARG_MESSAGE); + + progress = 0; + + messagingListener = new SimpleMessagingListener() { + @Override + public void updateProgress(int progress) { + updateDownloadProgress(progress); + } + }; + + MessagingController.getInstance(getActivity()).addDownloadProgressListener(messagingListener); + + dialog = new ProgressDialog(getActivity()); + dialog.setMessage(message); + dialog.setMax(size); + dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + dialog.setProgress(0); + dialog.show(); + + return dialog; + } + + @Override + public void onCancel(DialogInterface dialog) { + Activity activity = getActivity(); + if (activity != null && activity instanceof AttachmentDownloadCancelListener) { + AttachmentDownloadCancelListener listener = (AttachmentDownloadCancelListener) activity; + listener.onProgressCancel(this); + } + + super.onCancel(dialog); + } + + public interface AttachmentDownloadCancelListener { + void onProgressCancel(AttachmentDownloadDialogFragment fragment); + } + +} diff --git a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java index 35b7bf4cb..bbba057bf 100644 --- a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java +++ b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java @@ -37,6 +37,7 @@ import com.fsck.k9.activity.MessageLoaderHelper; import com.fsck.k9.activity.MessageLoaderHelper.MessageLoaderCallbacks; import com.fsck.k9.activity.MessageReference; import com.fsck.k9.controller.MessagingController; +import com.fsck.k9.fragment.AttachmentDownloadDialogFragment; import com.fsck.k9.fragment.ConfirmationDialogFragment; import com.fsck.k9.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener; import com.fsck.k9.fragment.ProgressDialogFragment; @@ -534,7 +535,8 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF } case R.id.dialog_attachment_progress: { String message = getString(R.string.dialog_attachment_progress_title); - fragment = ProgressDialogFragment.newInstance(null, message); + int size = (int) currentAttachmentViewInfo.size; + fragment = AttachmentDownloadDialogFragment.newInstance(size, message); break; } default: { @@ -796,7 +798,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF @Override public void onViewAttachment(AttachmentViewInfo attachment) { //TODO: check if we have to download the attachment first - + currentAttachmentViewInfo = attachment; getAttachmentController(attachment).viewAttachment(); }