Add percentage progress bar for attachment downloads

This commit is contained in:
harjot-oberai 2017-03-02 04:48:28 +05:30 committed by cketti
parent 24c8c25eef
commit 2accaae901
13 changed files with 170 additions and 17 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<ImapMessage> {
}
@Override
public void fetchPart(Message message, Part part, MessageRetrievalListener<Message> listener)
public void fetchPart(Message message, Part part, MessageRetrievalListener<Message> listener, AttachmentProgressCallback progressCallback)
throws MessagingException {
checkOpen();
@ -800,7 +801,7 @@ class ImapFolder extends Folder<ImapMessage> {
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<ImapMessage> {
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");

View file

@ -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<Body> bodyArgumentCaptor = ArgumentCaptor.forClass(Body.class);
verify(part).setBody(bodyArgumentCaptor.capture());

View file

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

View file

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

View file

@ -73,4 +73,6 @@ public interface MessagingListener {
void remoteSearchFailed(String folder, String err);
void enableProgressIndicator(boolean enable);
void updateProgress(int progress);
}

View file

@ -181,4 +181,9 @@ public abstract class SimpleMessagingListener implements MessagingListener {
@Override
public void enableProgressIndicator(boolean enable) {
}
@Override
public void updateProgress(int progress) {
}
}

View file

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

View file

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