Change the way we're tracking attachment download progress

This commit is contained in:
cketti 2017-03-26 08:33:28 +02:00
parent 2accaae901
commit bb16b1da3b
12 changed files with 146 additions and 101 deletions

View file

@ -1,5 +0,0 @@
package com.fsck.k9.mail;
public interface AttachmentProgressCallback {
void onUpdate(int progress);
}

View file

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

View file

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

View file

@ -127,9 +127,8 @@ public abstract class Folder<T extends Message> {
public abstract void fetch(List<T> messages, FetchProfile fp,
MessageRetrievalListener<T> listener) throws MessagingException;
public void fetchPart(Message message, Part part,
MessageRetrievalListener<Message> listener,
AttachmentProgressCallback progressCallback) throws MessagingException {
public void fetchPart(Message message, Part part, MessageRetrievalListener<Message> listener,
BodyFactory bodyFactory) throws MessagingException {
// This is causing trouble. Disabled for now. See issue 1733
//throw new RuntimeException("fetchPart() not implemented.");

View file

@ -19,7 +19,9 @@ import android.support.annotation.NonNull;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.BodyFactory;
import com.fsck.k9.mail.BodyPart;
import com.fsck.k9.mail.DefaultBodyFactory;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Multipart;
@ -106,7 +108,7 @@ public class MimeMessage extends Message {
// REALLY long References: headers
parserConfig.setMaxHeaderCount(-1); // Disable the check for header count.
MimeStreamParser parser = new MimeStreamParser(parserConfig);
parser.setContentHandler(new MimeMessageBuilder());
parser.setContentHandler(new MimeMessageBuilder(new DefaultBodyFactory()));
if (recurse) {
parser.setRecurse();
}
@ -520,8 +522,10 @@ public class MimeMessage extends Message {
private class MimeMessageBuilder implements ContentHandler {
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) {
@ -576,7 +580,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(), null);
Body body = bodyFactory.createBody(bd.getTransferEncoding(), bd.getMimeType(), in);
((Part)stack.peek()).setBody(body);
}

View file

@ -4,15 +4,11 @@ package com.fsck.k9.mail.internet;
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;
@ -20,8 +16,6 @@ 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;
@ -990,44 +984,6 @@ public class MimeUtility {
return isSameMimeType(mimeType, DEFAULT_ATTACHMENT_MIME_TYPE);
}
public static Body createBody(InputStream in, String contentTransferEncoding, String contentType, final AttachmentProgressCallback progressCallback)
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 out = tempBody.getOutputStream();
final CountingOutputStream countingOutputStream = new CountingOutputStream(out);
Timer timer = null;
try {
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();
}
return tempBody;
}
/**
* Get decoded contents of a body.
* <p/>

View file

@ -3,42 +3,31 @@ package com.fsck.k9.mail.store.imap;
import java.io.IOException;
import com.fsck.k9.mail.AttachmentProgressCallback;
import com.fsck.k9.mail.BodyFactory;
import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.filter.FixedLengthInputStream;
import com.fsck.k9.mail.internet.MimeHeader;
import com.fsck.k9.mail.internet.MimeUtility;
class FetchPartCallback implements ImapResponseCallback {
private Part mPart;
private AttachmentProgressCallback attachmentProgressCallback;
private final Part part;
private final BodyFactory bodyFactory;
FetchPartCallback(Part part) {
mPart = part;
}
FetchPartCallback(Part part, AttachmentProgressCallback attachmentProgressCallback) {
mPart = part;
this.attachmentProgressCallback = attachmentProgressCallback;
FetchPartCallback(Part part, BodyFactory bodyFactory) {
this.part = part;
this.bodyFactory = bodyFactory;
}
@Override
public Object foundLiteral(ImapResponse response, FixedLengthInputStream literal) throws IOException {
if (response.getTag() == null &&
ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
if (response.getTag() == null && ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
//TODO: check for correct UID
String contentTransferEncoding = mPart
.getHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
String contentType = mPart
.getHeader(MimeHeader.HEADER_CONTENT_TYPE)[0];
String contentTransferEncoding = part.getHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
String contentType = part.getHeader(MimeHeader.HEADER_CONTENT_TYPE)[0];
if (attachmentProgressCallback != null) {
return MimeUtility.createBody(literal, contentTransferEncoding, contentType, attachmentProgressCallback);
}
return MimeUtility.createBody(literal, contentTransferEncoding, contentType, null);
return bodyFactory.createBody(contentTransferEncoding, contentType, literal);
}
return null;
}

View file

@ -18,8 +18,8 @@ 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.BodyFactory;
import com.fsck.k9.mail.FetchProfile;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder;
@ -780,8 +780,8 @@ class ImapFolder extends Folder<ImapMessage> {
}
@Override
public void fetchPart(Message message, Part part, MessageRetrievalListener<Message> listener, AttachmentProgressCallback progressCallback)
throws MessagingException {
public void fetchPart(Message message, Part part, MessageRetrievalListener<Message> listener,
BodyFactory bodyFactory) throws MessagingException {
checkOpen();
String partId = part.getServerExtra();
@ -801,7 +801,7 @@ class ImapFolder extends Folder<ImapMessage> {
ImapResponse response;
int messageNumber = 0;
ImapResponseCallback callback = new FetchPartCallback(part, progressCallback);
ImapResponseCallback callback = new FetchPartCallback(part, bodyFactory);
do {
response = connection.readResponse(callback);
@ -829,7 +829,7 @@ class ImapFolder extends Folder<ImapMessage> {
if (literal != null) {
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);
} else if (literal instanceof String) {
String bodyString = (String) literal;
@ -838,8 +838,8 @@ class ImapFolder extends Folder<ImapMessage> {
String contentTransferEncoding =
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, progressCallback));
Body body = bodyFactory.createBody(contentTransferEncoding, contentType, bodyStream);
MimeMessageHelper.setBody(part, body);
} else {
// This shouldn't happen
throw new MessagingException("Got FETCH response with bogus parameters");

View file

@ -12,6 +12,7 @@ import java.util.Set;
import java.util.TimeZone;
import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.DefaultBodyFactory;
import com.fsck.k9.mail.FetchProfile;
import com.fsck.k9.mail.FetchProfile.Item;
import com.fsck.k9.mail.Flag;
@ -962,7 +963,7 @@ public class ImapFolderTest {
Part part = createPlainTextPart("1.1");
setupSingleFetchResponseToCallback();
folder.fetchPart(message, part, null, null);
folder.fetchPart(message, part, null, new DefaultBodyFactory());
ArgumentCaptor<Body> bodyArgumentCaptor = ArgumentCaptor.forClass(Body.class);
verify(part).setBody(bodyArgumentCaptor.capture());

View file

@ -18,8 +18,6 @@ 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;
@ -65,12 +63,13 @@ 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.controller.ProgressBodyFactory.ProgressListener;
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.BodyFactory;
import com.fsck.k9.mail.CertificateValidationException;
import com.fsck.k9.mail.DefaultBodyFactory;
import com.fsck.k9.mail.FetchProfile;
import com.fsck.k9.mail.FetchProfile.Item;
import com.fsck.k9.mail.Flag;
@ -154,8 +153,6 @@ 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) {
@ -265,10 +262,6 @@ public class MessagingController {
throw new Error(e);
}
public void addDownloadProgressListener(MessagingListener messagingListener){
attachmentDownloadProgressListener = messagingListener;
}
public void addListener(MessagingListener listener) {
listeners.add(listener);
refreshListener(listener);
@ -1492,8 +1485,9 @@ public class MessagingController {
/*
* Now download the parts we're interested in storing.
*/
BodyFactory bodyFactory = new DefaultBodyFactory();
for (Part part : viewables) {
remoteFolder.fetchPart(message, part, null, null);
remoteFolder.fetchPart(message, part, null, bodyFactory);
}
// Store the updated message locally
localFolder.appendMessages(Collections.singletonList(message));
@ -2561,15 +2555,17 @@ public class MessagingController {
remoteFolder = remoteStore.getFolder(folderName);
remoteFolder.open(Folder.OPEN_MODE_RW);
AttachmentProgressCallback attachmentProgressCallback = new AttachmentProgressCallback() {
ProgressBodyFactory bodyFactory = new ProgressBodyFactory(new ProgressListener() {
@Override
public void onUpdate(int progress) {
attachmentDownloadProgressListener.updateProgress(progress);
public void updateProgress(int progress) {
for (MessagingListener listener : getListeners()) {
listener.updateProgress(progress);
}
}
};
});
Message remoteMessage = remoteFolder.getMessage(message.getUid());
remoteFolder.fetchPart(remoteMessage, part, null, attachmentProgressCallback);
remoteFolder.fetchPart(remoteMessage, part, null, bodyFactory);
localFolder.addPartToMessage(message, part);

View file

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

View file

@ -21,6 +21,7 @@ public class AttachmentDownloadDialogFragment extends DialogFragment {
protected static final String ARG_SIZE = "size";
protected static final String ARG_MESSAGE = "message";
private MessagingController messagingController;
public static AttachmentDownloadDialogFragment newInstance(int size, String message) {
AttachmentDownloadDialogFragment attachmentDownloadDialogFragment = new AttachmentDownloadDialogFragment();
@ -53,7 +54,8 @@ public class AttachmentDownloadDialogFragment extends DialogFragment {
}
};
MessagingController.getInstance(getActivity()).addDownloadProgressListener(messagingListener);
messagingController = MessagingController.getInstance(getActivity());
messagingController.addListener(messagingListener);
dialog = new ProgressDialog(getActivity());
dialog.setMessage(message);
@ -65,6 +67,12 @@ public class AttachmentDownloadDialogFragment extends DialogFragment {
return dialog;
}
@Override
public void onDestroyView() {
messagingController.removeListener(messagingListener);
super.onDestroyView();
}
@Override
public void onCancel(DialogInterface dialog) {
Activity activity = getActivity();