Merge pull request #2500 from k9mail/GH-999_attachment_download_progress
Show progress for attachment downloads
This commit is contained in:
commit
0627ff5f87
19 changed files with 268 additions and 73 deletions
|
@ -0,0 +1,10 @@
|
||||||
|
package com.fsck.k9.mail;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
|
||||||
|
public interface BodyFactory {
|
||||||
|
Body createBody(String contentTransferEncoding, String contentType, InputStream inputStream) throws IOException;
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.fsck.k9.mail;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.internet.BinaryTempFileBody;
|
||||||
|
import com.fsck.k9.mail.internet.BinaryTempFileMessageBody;
|
||||||
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
|
|
||||||
|
public class DefaultBodyFactory implements BodyFactory {
|
||||||
|
public Body createBody(String contentTransferEncoding, String contentType, InputStream inputStream)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
if (contentTransferEncoding != null) {
|
||||||
|
contentTransferEncoding = MimeUtility.getHeaderParameter(contentTransferEncoding, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
final BinaryTempFileBody tempBody;
|
||||||
|
if (MimeUtil.isMessage(contentType)) {
|
||||||
|
tempBody = new BinaryTempFileMessageBody(contentTransferEncoding);
|
||||||
|
} else {
|
||||||
|
tempBody = new BinaryTempFileBody(contentTransferEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream outputStream = tempBody.getOutputStream();
|
||||||
|
try {
|
||||||
|
copyData(inputStream, outputStream);
|
||||||
|
} finally {
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void copyData(InputStream inputStream, OutputStream outputStream) throws IOException {
|
||||||
|
IOUtils.copy(inputStream, outputStream);
|
||||||
|
}
|
||||||
|
}
|
|
@ -127,8 +127,8 @@ public abstract class Folder<T extends Message> {
|
||||||
public abstract void fetch(List<T> messages, FetchProfile fp,
|
public abstract void fetch(List<T> messages, FetchProfile fp,
|
||||||
MessageRetrievalListener<T> listener) throws MessagingException;
|
MessageRetrievalListener<T> listener) throws MessagingException;
|
||||||
|
|
||||||
public void fetchPart(Message message, Part part,
|
public void fetchPart(Message message, Part part, MessageRetrievalListener<Message> listener,
|
||||||
MessageRetrievalListener<Message> listener) throws MessagingException {
|
BodyFactory bodyFactory) throws MessagingException {
|
||||||
// This is causing trouble. Disabled for now. See issue 1733
|
// This is causing trouble. Disabled for now. See issue 1733
|
||||||
//throw new RuntimeException("fetchPart() not implemented.");
|
//throw new RuntimeException("fetchPart() not implemented.");
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,9 @@ import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
|
import com.fsck.k9.mail.BodyFactory;
|
||||||
import com.fsck.k9.mail.BodyPart;
|
import com.fsck.k9.mail.BodyPart;
|
||||||
|
import com.fsck.k9.mail.DefaultBodyFactory;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Multipart;
|
import com.fsck.k9.mail.Multipart;
|
||||||
|
@ -106,7 +108,7 @@ public class MimeMessage extends Message {
|
||||||
// REALLY long References: headers
|
// REALLY long References: headers
|
||||||
parserConfig.setMaxHeaderCount(-1); // Disable the check for header count.
|
parserConfig.setMaxHeaderCount(-1); // Disable the check for header count.
|
||||||
MimeStreamParser parser = new MimeStreamParser(parserConfig);
|
MimeStreamParser parser = new MimeStreamParser(parserConfig);
|
||||||
parser.setContentHandler(new MimeMessageBuilder());
|
parser.setContentHandler(new MimeMessageBuilder(new DefaultBodyFactory()));
|
||||||
if (recurse) {
|
if (recurse) {
|
||||||
parser.setRecurse();
|
parser.setRecurse();
|
||||||
}
|
}
|
||||||
|
@ -520,8 +522,10 @@ public class MimeMessage extends Message {
|
||||||
|
|
||||||
private class MimeMessageBuilder implements ContentHandler {
|
private class MimeMessageBuilder implements ContentHandler {
|
||||||
private final LinkedList<Object> stack = new LinkedList<>();
|
private final LinkedList<Object> stack = new LinkedList<>();
|
||||||
|
private final BodyFactory bodyFactory;
|
||||||
|
|
||||||
public MimeMessageBuilder() {
|
public MimeMessageBuilder(BodyFactory bodyFactory) {
|
||||||
|
this.bodyFactory = bodyFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expect(Class<?> c) {
|
private void expect(Class<?> c) {
|
||||||
|
@ -576,7 +580,7 @@ public class MimeMessage extends Message {
|
||||||
@Override
|
@Override
|
||||||
public void body(BodyDescriptor bd, InputStream in) throws IOException, MimeException {
|
public void body(BodyDescriptor bd, InputStream in) throws IOException, MimeException {
|
||||||
expect(Part.class);
|
expect(Part.class);
|
||||||
Body body = MimeUtility.createBody(in, bd.getTransferEncoding(), bd.getMimeType());
|
Body body = bodyFactory.createBody(bd.getTransferEncoding(), bd.getMimeType(), in);
|
||||||
((Part)stack.peek()).setBody(body);
|
((Part)stack.peek()).setBody(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ package com.fsck.k9.mail.internet;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -16,7 +15,7 @@ import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Multipart;
|
import com.fsck.k9.mail.Multipart;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
import org.apache.james.mime4j.codec.Base64InputStream;
|
import org.apache.james.mime4j.codec.Base64InputStream;
|
||||||
import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
|
import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
|
||||||
import org.apache.james.mime4j.util.MimeUtil;
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
@ -985,30 +984,6 @@ public class MimeUtility {
|
||||||
return isSameMimeType(mimeType, DEFAULT_ATTACHMENT_MIME_TYPE);
|
return isSameMimeType(mimeType, DEFAULT_ATTACHMENT_MIME_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Body createBody(InputStream in, String contentTransferEncoding, String contentType)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
if (contentTransferEncoding != null) {
|
|
||||||
contentTransferEncoding = MimeUtility.getHeaderParameter(contentTransferEncoding, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
BinaryTempFileBody tempBody;
|
|
||||||
if (MimeUtil.isMessage(contentType)) {
|
|
||||||
tempBody = new BinaryTempFileMessageBody(contentTransferEncoding);
|
|
||||||
} else {
|
|
||||||
tempBody = new BinaryTempFileBody(contentTransferEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputStream out = tempBody.getOutputStream();
|
|
||||||
try {
|
|
||||||
IOUtils.copy(in, out);
|
|
||||||
} finally {
|
|
||||||
out.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return tempBody;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get decoded contents of a body.
|
* Get decoded contents of a body.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
|
|
@ -3,31 +3,31 @@ package com.fsck.k9.mail.store.imap;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.BodyFactory;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
import com.fsck.k9.mail.filter.FixedLengthInputStream;
|
import com.fsck.k9.mail.filter.FixedLengthInputStream;
|
||||||
import com.fsck.k9.mail.internet.MimeHeader;
|
import com.fsck.k9.mail.internet.MimeHeader;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
|
||||||
|
|
||||||
|
|
||||||
class FetchPartCallback implements ImapResponseCallback {
|
class FetchPartCallback implements ImapResponseCallback {
|
||||||
private Part mPart;
|
private final Part part;
|
||||||
|
private final BodyFactory bodyFactory;
|
||||||
|
|
||||||
FetchPartCallback(Part part) {
|
|
||||||
mPart = part;
|
FetchPartCallback(Part part, BodyFactory bodyFactory) {
|
||||||
|
this.part = part;
|
||||||
|
this.bodyFactory = bodyFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object foundLiteral(ImapResponse response, FixedLengthInputStream literal) throws IOException {
|
public Object foundLiteral(ImapResponse response, FixedLengthInputStream literal) throws IOException {
|
||||||
if (response.getTag() == null &&
|
if (response.getTag() == null && ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
|
||||||
ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
|
|
||||||
//TODO: check for correct UID
|
//TODO: check for correct UID
|
||||||
|
|
||||||
String contentTransferEncoding = mPart
|
String contentTransferEncoding = part.getHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
|
||||||
.getHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
|
String contentType = part.getHeader(MimeHeader.HEADER_CONTENT_TYPE)[0];
|
||||||
String contentType = mPart
|
|
||||||
.getHeader(MimeHeader.HEADER_CONTENT_TYPE)[0];
|
|
||||||
|
|
||||||
return MimeUtility.createBody(literal, contentTransferEncoding, contentType);
|
return bodyFactory.createBody(contentTransferEncoding, contentType, literal);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.fsck.k9.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
|
import com.fsck.k9.mail.BodyFactory;
|
||||||
import com.fsck.k9.mail.FetchProfile;
|
import com.fsck.k9.mail.FetchProfile;
|
||||||
import com.fsck.k9.mail.Flag;
|
import com.fsck.k9.mail.Flag;
|
||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
|
@ -779,8 +780,8 @@ class ImapFolder extends Folder<ImapMessage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fetchPart(Message message, Part part, MessageRetrievalListener<Message> listener)
|
public void fetchPart(Message message, Part part, MessageRetrievalListener<Message> listener,
|
||||||
throws MessagingException {
|
BodyFactory bodyFactory) throws MessagingException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
|
|
||||||
String partId = part.getServerExtra();
|
String partId = part.getServerExtra();
|
||||||
|
@ -800,7 +801,7 @@ class ImapFolder extends Folder<ImapMessage> {
|
||||||
ImapResponse response;
|
ImapResponse response;
|
||||||
int messageNumber = 0;
|
int messageNumber = 0;
|
||||||
|
|
||||||
ImapResponseCallback callback = new FetchPartCallback(part);
|
ImapResponseCallback callback = new FetchPartCallback(part, bodyFactory);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
response = connection.readResponse(callback);
|
response = connection.readResponse(callback);
|
||||||
|
@ -828,7 +829,7 @@ class ImapFolder extends Folder<ImapMessage> {
|
||||||
|
|
||||||
if (literal != null) {
|
if (literal != null) {
|
||||||
if (literal instanceof Body) {
|
if (literal instanceof Body) {
|
||||||
// Most of the work was done in FetchAttchmentCallback.foundLiteral()
|
// Most of the work was done in FetchAttachmentCallback.foundLiteral()
|
||||||
MimeMessageHelper.setBody(part, (Body) literal);
|
MimeMessageHelper.setBody(part, (Body) literal);
|
||||||
} else if (literal instanceof String) {
|
} else if (literal instanceof String) {
|
||||||
String bodyString = (String) literal;
|
String bodyString = (String) literal;
|
||||||
|
@ -837,8 +838,8 @@ class ImapFolder extends Folder<ImapMessage> {
|
||||||
String contentTransferEncoding =
|
String contentTransferEncoding =
|
||||||
part.getHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
|
part.getHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
|
||||||
String contentType = part.getHeader(MimeHeader.HEADER_CONTENT_TYPE)[0];
|
String contentType = part.getHeader(MimeHeader.HEADER_CONTENT_TYPE)[0];
|
||||||
MimeMessageHelper.setBody(part, MimeUtility.createBody(bodyStream, contentTransferEncoding,
|
Body body = bodyFactory.createBody(contentTransferEncoding, contentType, bodyStream);
|
||||||
contentType));
|
MimeMessageHelper.setBody(part, body);
|
||||||
} else {
|
} else {
|
||||||
// This shouldn't happen
|
// This shouldn't happen
|
||||||
throw new MessagingException("Got FETCH response with bogus parameters");
|
throw new MessagingException("Got FETCH response with bogus parameters");
|
||||||
|
|
|
@ -12,6 +12,7 @@ import java.util.Set;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import com.fsck.k9.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
|
import com.fsck.k9.mail.DefaultBodyFactory;
|
||||||
import com.fsck.k9.mail.FetchProfile;
|
import com.fsck.k9.mail.FetchProfile;
|
||||||
import com.fsck.k9.mail.FetchProfile.Item;
|
import com.fsck.k9.mail.FetchProfile.Item;
|
||||||
import com.fsck.k9.mail.Flag;
|
import com.fsck.k9.mail.Flag;
|
||||||
|
@ -934,7 +935,7 @@ public class ImapFolderTest {
|
||||||
Part part = createPart("TEXT");
|
Part part = createPart("TEXT");
|
||||||
when(imapConnection.readResponse(any(ImapResponseCallback.class))).thenReturn(createImapResponse("x OK"));
|
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);
|
verify(imapConnection).sendCommand("UID FETCH 1 (UID BODY.PEEK[TEXT]<0.4096>)", false);
|
||||||
}
|
}
|
||||||
|
@ -948,7 +949,7 @@ public class ImapFolderTest {
|
||||||
Part part = createPart("1.1");
|
Part part = createPart("1.1");
|
||||||
when(imapConnection.readResponse(any(ImapResponseCallback.class))).thenReturn(createImapResponse("x OK"));
|
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);
|
verify(imapConnection).sendCommand("UID FETCH 1 (UID BODY.PEEK[1.1])", false);
|
||||||
}
|
}
|
||||||
|
@ -962,7 +963,7 @@ public class ImapFolderTest {
|
||||||
Part part = createPlainTextPart("1.1");
|
Part part = createPlainTextPart("1.1");
|
||||||
setupSingleFetchResponseToCallback();
|
setupSingleFetchResponseToCallback();
|
||||||
|
|
||||||
folder.fetchPart(message, part, null);
|
folder.fetchPart(message, part, null, new DefaultBodyFactory());
|
||||||
|
|
||||||
ArgumentCaptor<Body> bodyArgumentCaptor = ArgumentCaptor.forClass(Body.class);
|
ArgumentCaptor<Body> bodyArgumentCaptor = ArgumentCaptor.forClass(Body.class);
|
||||||
verify(part).setBody(bodyArgumentCaptor.capture());
|
verify(part).setBody(bodyArgumentCaptor.capture());
|
||||||
|
|
|
@ -69,8 +69,11 @@ import com.fsck.k9.activity.misc.Attachment;
|
||||||
import com.fsck.k9.controller.MessagingController;
|
import com.fsck.k9.controller.MessagingController;
|
||||||
import com.fsck.k9.controller.MessagingListener;
|
import com.fsck.k9.controller.MessagingListener;
|
||||||
import com.fsck.k9.controller.SimpleMessagingListener;
|
import com.fsck.k9.controller.SimpleMessagingListener;
|
||||||
|
import com.fsck.k9.fragment.AttachmentDownloadDialogFragment;
|
||||||
import com.fsck.k9.fragment.ProgressDialogFragment;
|
import com.fsck.k9.fragment.ProgressDialogFragment;
|
||||||
import com.fsck.k9.fragment.ProgressDialogFragment.CancelListener;
|
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.Contacts;
|
||||||
import com.fsck.k9.helper.IdentityHelper;
|
import com.fsck.k9.helper.IdentityHelper;
|
||||||
import com.fsck.k9.helper.MailTo;
|
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
|
@SuppressWarnings("deprecation") // TODO get rid of activity dialogs and indeterminate progress bars
|
||||||
public class MessageCompose extends K9Activity implements OnClickListener,
|
public class MessageCompose extends K9Activity implements OnClickListener,
|
||||||
CancelListener, OnFocusChangeListener, OnCryptoModeChangedListener,
|
CancelListener, AttachmentDownloadCancelListener, OnFocusChangeListener, OnCryptoModeChangedListener,
|
||||||
OnOpenPgpInlineChangeListener, OnOpenPgpSignOnlyChangeListener, MessageBuilder.Callback,
|
OnOpenPgpInlineChangeListener, OnOpenPgpSignOnlyChangeListener, MessageBuilder.Callback,
|
||||||
AttachmentPresenter.AttachmentsChangedListener, RecipientPresenter.RecipientsChangedListener {
|
AttachmentPresenter.AttachmentsChangedListener, RecipientPresenter.RecipientsChangedListener {
|
||||||
|
|
||||||
|
@ -1053,6 +1056,10 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgressCancel(AttachmentDownloadDialogFragment fragment) {
|
||||||
|
attachmentPresenter.attachmentProgressDialogCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
public void onProgressCancel(ProgressDialogFragment fragment) {
|
public void onProgressCancel(ProgressDialogFragment fragment) {
|
||||||
attachmentPresenter.attachmentProgressDialogCancelled();
|
attachmentPresenter.attachmentProgressDialogCancelled();
|
||||||
|
|
|
@ -162,7 +162,7 @@ public class AttachmentPresenter {
|
||||||
if (attachmentViewInfo.inlineAttachment) {
|
if (attachmentViewInfo.inlineAttachment) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!attachmentViewInfo.isContentAvailable) {
|
if (!attachmentViewInfo.isContentAvailable()) {
|
||||||
allPartsAvailable = false;
|
allPartsAvailable = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,10 +63,13 @@ import com.fsck.k9.controller.MessagingControllerCommands.PendingExpunge;
|
||||||
import com.fsck.k9.controller.MessagingControllerCommands.PendingMarkAllAsRead;
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingMarkAllAsRead;
|
||||||
import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveOrCopy;
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveOrCopy;
|
||||||
import com.fsck.k9.controller.MessagingControllerCommands.PendingSetFlag;
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingSetFlag;
|
||||||
|
import com.fsck.k9.controller.ProgressBodyFactory.ProgressListener;
|
||||||
import com.fsck.k9.helper.Contacts;
|
import com.fsck.k9.helper.Contacts;
|
||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.AuthenticationFailedException;
|
import com.fsck.k9.mail.AuthenticationFailedException;
|
||||||
|
import com.fsck.k9.mail.BodyFactory;
|
||||||
import com.fsck.k9.mail.CertificateValidationException;
|
import com.fsck.k9.mail.CertificateValidationException;
|
||||||
|
import com.fsck.k9.mail.DefaultBodyFactory;
|
||||||
import com.fsck.k9.mail.FetchProfile;
|
import com.fsck.k9.mail.FetchProfile;
|
||||||
import com.fsck.k9.mail.FetchProfile.Item;
|
import com.fsck.k9.mail.FetchProfile.Item;
|
||||||
import com.fsck.k9.mail.Flag;
|
import com.fsck.k9.mail.Flag;
|
||||||
|
@ -1482,8 +1485,9 @@ public class MessagingController {
|
||||||
/*
|
/*
|
||||||
* Now download the parts we're interested in storing.
|
* Now download the parts we're interested in storing.
|
||||||
*/
|
*/
|
||||||
|
BodyFactory bodyFactory = new DefaultBodyFactory();
|
||||||
for (Part part : viewables) {
|
for (Part part : viewables) {
|
||||||
remoteFolder.fetchPart(message, part, null);
|
remoteFolder.fetchPart(message, part, null, bodyFactory);
|
||||||
}
|
}
|
||||||
// Store the updated message locally
|
// Store the updated message locally
|
||||||
localFolder.appendMessages(Collections.singletonList(message));
|
localFolder.appendMessages(Collections.singletonList(message));
|
||||||
|
@ -2551,8 +2555,17 @@ public class MessagingController {
|
||||||
remoteFolder = remoteStore.getFolder(folderName);
|
remoteFolder = remoteStore.getFolder(folderName);
|
||||||
remoteFolder.open(Folder.OPEN_MODE_RW);
|
remoteFolder.open(Folder.OPEN_MODE_RW);
|
||||||
|
|
||||||
|
ProgressBodyFactory bodyFactory = new ProgressBodyFactory(new ProgressListener() {
|
||||||
|
@Override
|
||||||
|
public void updateProgress(int progress) {
|
||||||
|
for (MessagingListener listener : getListeners()) {
|
||||||
|
listener.updateProgress(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Message remoteMessage = remoteFolder.getMessage(message.getUid());
|
Message remoteMessage = remoteFolder.getMessage(message.getUid());
|
||||||
remoteFolder.fetchPart(remoteMessage, part, null);
|
remoteFolder.fetchPart(remoteMessage, part, null, bodyFactory);
|
||||||
|
|
||||||
localFolder.addPartToMessage(message, part);
|
localFolder.addPartToMessage(message, part);
|
||||||
|
|
||||||
|
|
|
@ -73,4 +73,6 @@ public interface MessagingListener {
|
||||||
void remoteSearchFailed(String folder, String err);
|
void remoteSearchFailed(String folder, String err);
|
||||||
|
|
||||||
void enableProgressIndicator(boolean enable);
|
void enableProgressIndicator(boolean enable);
|
||||||
|
|
||||||
|
void updateProgress(int progress);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.fsck.k9.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.DefaultBodyFactory;
|
||||||
|
import org.apache.commons.io.output.CountingOutputStream;
|
||||||
|
|
||||||
|
|
||||||
|
class ProgressBodyFactory extends DefaultBodyFactory {
|
||||||
|
private final ProgressListener progressListener;
|
||||||
|
|
||||||
|
|
||||||
|
ProgressBodyFactory(ProgressListener progressListener) {
|
||||||
|
this.progressListener = progressListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void copyData(InputStream inputStream, OutputStream outputStream) throws IOException {
|
||||||
|
final CountingOutputStream countingOutputStream = new CountingOutputStream(outputStream);
|
||||||
|
|
||||||
|
Timer timer = new Timer();
|
||||||
|
try {
|
||||||
|
timer.scheduleAtFixedRate(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
progressListener.updateProgress(countingOutputStream.getCount());
|
||||||
|
}
|
||||||
|
}, 0, 50);
|
||||||
|
|
||||||
|
super.copyData(inputStream, countingOutputStream);
|
||||||
|
} finally {
|
||||||
|
timer.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProgressListener {
|
||||||
|
void updateProgress(int progress);
|
||||||
|
}
|
||||||
|
}
|
|
@ -181,4 +181,9 @@ public abstract class SimpleMessagingListener implements MessagingListener {
|
||||||
@Override
|
@Override
|
||||||
public void enableProgressIndicator(boolean enable) {
|
public void enableProgressIndicator(boolean enable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateProgress(int progress) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
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 {
|
||||||
|
private static final String ARG_SIZE = "size";
|
||||||
|
private static final String ARG_MESSAGE = "message";
|
||||||
|
|
||||||
|
|
||||||
|
private ProgressDialog dialog;
|
||||||
|
private MessagingListener messagingListener;
|
||||||
|
private MessagingController messagingController;
|
||||||
|
|
||||||
|
|
||||||
|
public static AttachmentDownloadDialogFragment newInstance(int size, String message) {
|
||||||
|
AttachmentDownloadDialogFragment fragment = new AttachmentDownloadDialogFragment();
|
||||||
|
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putInt(ARG_SIZE, size);
|
||||||
|
args.putString(ARG_MESSAGE, message);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
Bundle args = getArguments();
|
||||||
|
int size = args.getInt(ARG_SIZE);
|
||||||
|
String message = args.getString(ARG_MESSAGE);
|
||||||
|
|
||||||
|
messagingListener = new SimpleMessagingListener() {
|
||||||
|
@Override
|
||||||
|
public void updateProgress(int progress) {
|
||||||
|
dialog.setProgress(progress);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
messagingController = MessagingController.getInstance(getActivity());
|
||||||
|
messagingController.addListener(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 onDestroyView() {
|
||||||
|
messagingController.removeListener(messagingListener);
|
||||||
|
super.onDestroyView();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,16 +21,24 @@ public class AttachmentViewInfo {
|
||||||
public final Uri internalUri;
|
public final Uri internalUri;
|
||||||
public final boolean inlineAttachment;
|
public final boolean inlineAttachment;
|
||||||
public final Part part;
|
public final Part part;
|
||||||
public final boolean isContentAvailable;
|
private boolean contentAvailable;
|
||||||
|
|
||||||
public AttachmentViewInfo(String mimeType, String displayName, long size, Uri internalUri, boolean inlineAttachment,
|
public AttachmentViewInfo(String mimeType, String displayName, long size, Uri internalUri, boolean inlineAttachment,
|
||||||
Part part, boolean isContentAvailable) {
|
Part part, boolean contentAvailable) {
|
||||||
this.mimeType = mimeType;
|
this.mimeType = mimeType;
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.internalUri = internalUri;
|
this.internalUri = internalUri;
|
||||||
this.inlineAttachment = inlineAttachment;
|
this.inlineAttachment = inlineAttachment;
|
||||||
this.part = part;
|
this.part = part;
|
||||||
this.isContentAvailable = isContentAvailable;
|
this.contentAvailable = contentAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isContentAvailable() {
|
||||||
|
return contentAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentAvailable() {
|
||||||
|
this.contentAvailable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class AttachmentController {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void viewAttachment() {
|
public void viewAttachment() {
|
||||||
if (!attachment.isContentAvailable) {
|
if (!attachment.isContentAvailable()) {
|
||||||
downloadAndViewAttachment((LocalPart) attachment.part);
|
downloadAndViewAttachment((LocalPart) attachment.part);
|
||||||
} else {
|
} else {
|
||||||
viewLocalAttachment();
|
viewLocalAttachment();
|
||||||
|
@ -100,6 +100,7 @@ public class AttachmentController {
|
||||||
controller.loadAttachment(account, message, attachment.part, new SimpleMessagingListener() {
|
controller.loadAttachment(account, message, attachment.part, new SimpleMessagingListener() {
|
||||||
@Override
|
@Override
|
||||||
public void loadAttachmentFinished(Account account, Message message, Part part) {
|
public void loadAttachmentFinished(Account account, Message message, Part part) {
|
||||||
|
attachment.setContentAvailable();
|
||||||
messageViewFragment.hideAttachmentLoadingDialogOnMainThread();
|
messageViewFragment.hideAttachmentLoadingDialogOnMainThread();
|
||||||
messageViewFragment.runOnMainThread(attachmentDownloadedCallback);
|
messageViewFragment.runOnMainThread(attachmentDownloadedCallback);
|
||||||
}
|
}
|
||||||
|
@ -129,7 +130,7 @@ public class AttachmentController {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!attachment.isContentAvailable) {
|
if (!attachment.isContentAvailable()) {
|
||||||
downloadAndSaveAttachmentTo((LocalPart) attachment.part, directory);
|
downloadAndSaveAttachmentTo((LocalPart) attachment.part, directory);
|
||||||
} else {
|
} else {
|
||||||
saveLocalAttachmentTo(directory);
|
saveLocalAttachmentTo(directory);
|
||||||
|
|
|
@ -18,7 +18,6 @@ import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import timber.log.Timber;
|
|
||||||
import android.view.ContextThemeWrapper;
|
import android.view.ContextThemeWrapper;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -36,21 +35,22 @@ import com.fsck.k9.activity.ChooseFolder;
|
||||||
import com.fsck.k9.activity.MessageLoaderHelper;
|
import com.fsck.k9.activity.MessageLoaderHelper;
|
||||||
import com.fsck.k9.activity.MessageLoaderHelper.MessageLoaderCallbacks;
|
import com.fsck.k9.activity.MessageLoaderHelper.MessageLoaderCallbacks;
|
||||||
import com.fsck.k9.activity.MessageReference;
|
import com.fsck.k9.activity.MessageReference;
|
||||||
|
import com.fsck.k9.activity.setup.OpenPgpAppSelectDialog;
|
||||||
import com.fsck.k9.controller.MessagingController;
|
import com.fsck.k9.controller.MessagingController;
|
||||||
|
import com.fsck.k9.fragment.AttachmentDownloadDialogFragment;
|
||||||
import com.fsck.k9.fragment.ConfirmationDialogFragment;
|
import com.fsck.k9.fragment.ConfirmationDialogFragment;
|
||||||
import com.fsck.k9.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
|
import com.fsck.k9.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
|
||||||
import com.fsck.k9.fragment.ProgressDialogFragment;
|
|
||||||
import com.fsck.k9.helper.FileBrowserHelper;
|
import com.fsck.k9.helper.FileBrowserHelper;
|
||||||
import com.fsck.k9.helper.FileBrowserHelper.FileBrowserFailOverCallback;
|
import com.fsck.k9.helper.FileBrowserHelper.FileBrowserFailOverCallback;
|
||||||
import com.fsck.k9.mail.Flag;
|
import com.fsck.k9.mail.Flag;
|
||||||
import com.fsck.k9.mailstore.AttachmentViewInfo;
|
import com.fsck.k9.mailstore.AttachmentViewInfo;
|
||||||
import com.fsck.k9.mailstore.LocalMessage;
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
import com.fsck.k9.mailstore.MessageViewInfo;
|
import com.fsck.k9.mailstore.MessageViewInfo;
|
||||||
import com.fsck.k9.activity.setup.OpenPgpAppSelectDialog;
|
|
||||||
import com.fsck.k9.ui.messageview.CryptoInfoDialog.OnClickShowCryptoKeyListener;
|
import com.fsck.k9.ui.messageview.CryptoInfoDialog.OnClickShowCryptoKeyListener;
|
||||||
import com.fsck.k9.ui.messageview.MessageCryptoPresenter.MessageCryptoMvpView;
|
import com.fsck.k9.ui.messageview.MessageCryptoPresenter.MessageCryptoMvpView;
|
||||||
import com.fsck.k9.view.MessageCryptoDisplayStatus;
|
import com.fsck.k9.view.MessageCryptoDisplayStatus;
|
||||||
import com.fsck.k9.view.MessageHeader;
|
import com.fsck.k9.view.MessageHeader;
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
|
||||||
public class MessageViewFragment extends Fragment implements ConfirmationDialogFragmentListener,
|
public class MessageViewFragment extends Fragment implements ConfirmationDialogFragmentListener,
|
||||||
|
@ -534,7 +534,8 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
||||||
}
|
}
|
||||||
case R.id.dialog_attachment_progress: {
|
case R.id.dialog_attachment_progress: {
|
||||||
String message = getString(R.string.dialog_attachment_progress_title);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
@ -795,22 +796,18 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewAttachment(AttachmentViewInfo attachment) {
|
public void onViewAttachment(AttachmentViewInfo attachment) {
|
||||||
//TODO: check if we have to download the attachment first
|
currentAttachmentViewInfo = attachment;
|
||||||
|
|
||||||
getAttachmentController(attachment).viewAttachment();
|
getAttachmentController(attachment).viewAttachment();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveAttachment(AttachmentViewInfo attachment) {
|
public void onSaveAttachment(AttachmentViewInfo attachment) {
|
||||||
//TODO: check if we have to download the attachment first
|
currentAttachmentViewInfo = attachment;
|
||||||
|
|
||||||
getAttachmentController(attachment).saveAttachment();
|
getAttachmentController(attachment).saveAttachment();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveAttachmentToUserProvidedDirectory(final AttachmentViewInfo attachment) {
|
public void onSaveAttachmentToUserProvidedDirectory(final AttachmentViewInfo attachment) {
|
||||||
//TODO: check if we have to download the attachment first
|
|
||||||
|
|
||||||
currentAttachmentViewInfo = attachment;
|
currentAttachmentViewInfo = attachment;
|
||||||
FileBrowserHelper.getInstance().showFileBrowserActivity(MessageViewFragment.this, null,
|
FileBrowserHelper.getInstance().showFileBrowserActivity(MessageViewFragment.this, null,
|
||||||
ACTIVITY_CHOOSE_DIRECTORY, new FileBrowserFailOverCallback() {
|
ACTIVITY_CHOOSE_DIRECTORY, new FileBrowserFailOverCallback() {
|
||||||
|
|
|
@ -168,7 +168,7 @@ public class AttachmentInfoExtractorTest {
|
||||||
|
|
||||||
AttachmentViewInfo attachmentViewInfo = attachmentInfoExtractor.extractAttachmentInfoForDatabase(part);
|
AttachmentViewInfo attachmentViewInfo = attachmentInfoExtractor.extractAttachmentInfoForDatabase(part);
|
||||||
|
|
||||||
assertFalse(attachmentViewInfo.isContentAvailable);
|
assertFalse(attachmentViewInfo.isContentAvailable());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -178,7 +178,7 @@ public class AttachmentInfoExtractorTest {
|
||||||
|
|
||||||
AttachmentViewInfo attachmentViewInfo = attachmentInfoExtractor.extractAttachmentInfoForDatabase(part);
|
AttachmentViewInfo attachmentViewInfo = attachmentInfoExtractor.extractAttachmentInfoForDatabase(part);
|
||||||
|
|
||||||
assertTrue(attachmentViewInfo.isContentAvailable);
|
assertTrue(attachmentViewInfo.isContentAvailable());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -206,6 +206,6 @@ public class AttachmentInfoExtractorTest {
|
||||||
assertEquals(TEST_SIZE, attachmentViewInfo.size);
|
assertEquals(TEST_SIZE, attachmentViewInfo.size);
|
||||||
assertEquals(TEST_MIME_TYPE, attachmentViewInfo.mimeType);
|
assertEquals(TEST_MIME_TYPE, attachmentViewInfo.mimeType);
|
||||||
assertFalse(attachmentViewInfo.inlineAttachment);
|
assertFalse(attachmentViewInfo.inlineAttachment);
|
||||||
assertTrue(attachmentViewInfo.isContentAvailable);
|
assertTrue(attachmentViewInfo.isContentAvailable());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue