Loading app/core/src/main/java/com/fsck/k9/message/MessageBuilder.java +38 −4 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ package com.fsck.k9.message; import java.util.Date; import java.util.List; import java.util.Map; import android.app.Activity; import android.app.PendingIntent; Loading Loading @@ -56,6 +57,7 @@ public abstract class MessageBuilder { private SimpleMessageFormat messageFormat; private String text; private List<Attachment> attachments; private Map<String, Attachment> inlineAttachments; private String signature; private QuoteStyle quoteStyle; private QuotedTextMode quotedTextMode; Loading Loading @@ -169,7 +171,16 @@ public abstract class MessageBuilder { // Let the receiver select either the text or the HTML part. bodyPlain = buildText(isDraft, SimpleMessageFormat.TEXT); composedMimeMessage.addBodyPart(MimeBodyPart.create(bodyPlain, "text/plain")); composedMimeMessage.addBodyPart(MimeBodyPart.create(body, "text/html")); MimeBodyPart htmlPart = MimeBodyPart.create(body, "text/html"); if (inlineAttachments != null && inlineAttachments.size() > 0) { MimeMultipart htmlPartWithInlineImages = new MimeMultipart("multipart/related", boundaryGenerator.generateBoundary()); htmlPartWithInlineImages.addBodyPart(htmlPart); addInlineAttachmentsToMessage(htmlPartWithInlineImages); composedMimeMessage.addBodyPart(MimeBodyPart.create(htmlPartWithInlineImages)); } else { composedMimeMessage.addBodyPart(htmlPart); } if (hasAttachments) { // If we're HTML and have attachments, we have a MimeMultipart container to hold the Loading Loading @@ -236,7 +247,25 @@ public abstract class MessageBuilder { MimeBodyPart bp = MimeBodyPart.create(body); addContentType(bp, attachment.getContentType(), attachment.getName()); addContentDisposition(bp, attachment.getName(), attachment.getSize()); addContentDisposition(bp, "attachment", attachment.getName(), attachment.getSize()); mp.addBodyPart(bp); } } private void addInlineAttachmentsToMessage(final MimeMultipart mp) throws MessagingException { for (String cid : inlineAttachments.keySet()) { Attachment attachment = inlineAttachments.get(cid); if (attachment.getState() != Attachment.LoadingState.COMPLETE) { continue; } Body body = new TempFileBody(attachment.getFileName()); MimeBodyPart bp = MimeBodyPart.create(body); addContentType(bp, attachment.getContentType(), attachment.getName()); addContentDisposition(bp, "inline", attachment.getName(), attachment.getSize()); bp.addHeader(MimeHeader.HEADER_CONTENT_ID, cid); mp.addBodyPart(bp); } Loading @@ -251,8 +280,8 @@ public abstract class MessageBuilder { } } private void addContentDisposition(MimeBodyPart bodyPart, String fileName, Long size) { String value = Headers.contentDisposition("attachment", fileName, size); private void addContentDisposition(MimeBodyPart bodyPart, String disposition, String fileName, Long size) { String value = Headers.contentDisposition(disposition, fileName, size); bodyPart.addHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, value); } Loading Loading @@ -401,6 +430,11 @@ public abstract class MessageBuilder { return this; } public MessageBuilder setInlineAttachments(Map<String, Attachment> attachments) { this.inlineAttachments = attachments; return this; } public MessageBuilder setSignature(String signature) { this.signature = signature; return this; Loading app/core/src/test/java/com/fsck/k9/message/MessageBuilderTest.java +30 −0 Original line number Diff line number Diff line Loading @@ -9,7 +9,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import com.fsck.k9.Account.QuoteStyle; import com.fsck.k9.CoreResourceProvider; Loading @@ -23,6 +25,7 @@ import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.internet.MessageIdGenerator; import com.fsck.k9.mail.internet.MimeHeader; import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.message.MessageBuilder.Callback; Loading Loading @@ -275,6 +278,33 @@ public class MessageBuilderTest extends RobolectricTest { assertEquals("text/html", parts.get(1).getMimeType()); } @Test public void build_usingHtmlFormatWithInlineAttachment_shouldUseMultipartAlternativeInCorrectOrder() throws Exception { String contentId = "contentId"; String attachmentMimeType = "image/png"; Map<String, Attachment> inlineAttachments = new HashMap<>(); inlineAttachments.put(contentId, createAttachmentWithContent(attachmentMimeType, "1x1.png", "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC")); MessageBuilder messageBuilder = createHtmlMessageBuilder().setInlineAttachments(inlineAttachments); messageBuilder.buildAsync(callback); MimeMessage message = getMessageFromCallback(); assertEquals(MimeMultipart.class, message.getBody().getClass()); assertEquals("multipart/alternative", ((MimeMultipart) message.getBody()).getMimeType()); List<BodyPart> parts = ((MimeMultipart) message.getBody()).getBodyParts(); //RFC 2046 - 5.1.4. - Best type is last displayable assertEquals("text/plain", parts.get(0).getMimeType()); assertEquals("multipart/related", parts.get(1).getMimeType()); List<BodyPart> partWithInlineAttachment = ((MimeMultipart) parts.get(1).getBody()).getBodyParts(); assertEquals("text/html", partWithInlineAttachment.get(0).getMimeType()); assertEquals(attachmentMimeType, partWithInlineAttachment.get(1).getMimeType()); String[] attachmentHeaders = partWithInlineAttachment.get(1).getHeader(MimeHeader.HEADER_CONTENT_ID); assertEquals(1, attachmentHeaders.length); assertEquals(contentId, attachmentHeaders[0]); } @Test public void build_withMessageAttachment_shouldAttachAsMessageRfc822() throws Exception { MessageBuilder messageBuilder = createSimpleMessageBuilder(); Loading app/ui/legacy/build.gradle +1 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ dependencies { testImplementation project(':app:storage') testImplementation project(':app:testing') testImplementation "org.robolectric:robolectric:${versions.robolectric}" testImplementation "androidx.test:core:${versions.androidxTestCore}" testImplementation "junit:junit:${versions.junit}" testImplementation "com.google.truth:truth:${versions.truth}" testImplementation "org.mockito:mockito-core:${versions.mockito}" Loading app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageCompose.java +2 −1 Original line number Diff line number Diff line Loading @@ -713,6 +713,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, .setMessageFormat(currentMessageFormat) .setText(CrLfConverter.toCrLf(messageContentView.getText())) .setAttachments(attachmentPresenter.getAttachments()) .setInlineAttachments(attachmentPresenter.getInlineAttachments()) .setSignature(CrLfConverter.toCrLf(signatureView.getText())) .setSignatureBeforeQuotedText(account.isSignatureBeforeQuotedText()) .setIdentityChanged(identityChanged) Loading Loading @@ -1369,7 +1370,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, } if (!relatedMessageProcessed) { attachmentPresenter.loadNonInlineAttachments(messageViewInfo); attachmentPresenter.loadAllAvailableAttachments(messageViewInfo); } // Decode the identity header when loading a draft. Loading app/ui/legacy/src/main/java/com/fsck/k9/activity/compose/AttachmentPresenter.java +69 −9 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ package com.fsck.k9.activity.compose; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import android.app.Activity; import android.content.ClipData; Loading @@ -19,6 +20,7 @@ import com.fsck.k9.activity.compose.ComposeCryptoStatus.AttachErrorState; import com.fsck.k9.activity.loader.AttachmentContentLoader; import com.fsck.k9.activity.loader.AttachmentInfoLoader; import com.fsck.k9.activity.misc.Attachment; import com.fsck.k9.activity.misc.InlineAttachment; import com.fsck.k9.controller.MessageReference; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mailstore.AttachmentViewInfo; Loading Loading @@ -46,7 +48,8 @@ public class AttachmentPresenter { private final AttachmentsChangedListener listener; // persistent state private LinkedHashMap<Uri, Attachment> attachments; private final LinkedHashMap<Uri, Attachment> attachments; private final LinkedHashMap<Uri, InlineAttachment> inlineAttachments; private int nextLoaderId = 0; private WaitingAction actionToPerformAfterWaiting = WaitingAction.NONE; Loading @@ -59,6 +62,7 @@ public class AttachmentPresenter { this.listener = listener; attachments = new LinkedHashMap<>(); inlineAttachments = new LinkedHashMap<>(); } public void onSaveInstanceState(Bundle outState) { Loading Loading @@ -122,6 +126,14 @@ public class AttachmentPresenter { return new ArrayList<>(attachments.values()); } public Map<String, com.fsck.k9.message.Attachment> getInlineAttachments() { Map<String, com.fsck.k9.message.Attachment> result = new LinkedHashMap<>(); for (InlineAttachment attachment : inlineAttachments.values()) { result.put(attachment.getContentId(), attachment.getAttachment()); } return result; } public void onClickAddAttachment(RecipientPresenter recipientPresenter) { ComposeCryptoStatus currentCachedCryptoStatus = recipientPresenter.getCurrentCachedCryptoStatus(); if (currentCachedCryptoStatus == null) { Loading Loading @@ -159,6 +171,24 @@ public class AttachmentPresenter { addAttachment(uri, contentType, false, false); } private void addInlineAttachment(AttachmentViewInfo attachmentViewInfo) { if (inlineAttachments.containsKey(attachmentViewInfo.internalUri)) { throw new IllegalStateException("Received the same attachmentViewInfo twice!"); } int loaderId = getNextFreeLoaderId(); Attachment attachment = Attachment.createAttachment( attachmentViewInfo.internalUri, loaderId, attachmentViewInfo.mimeType, true, true); attachment = attachment.deriveWithMetadataLoaded( attachmentViewInfo.mimeType, attachmentViewInfo.displayName, attachmentViewInfo.size); inlineAttachments.put(attachment.uri, new InlineAttachment(attachmentViewInfo.part.getContentId(), attachment)); Bundle bundle = new Bundle(); bundle.putParcelable(LOADER_ARG_ATTACHMENT, attachment.uri); loaderManager.initLoader(attachment.loaderId, bundle, mInlineAttachmentContentLoaderCallback); } private void addInternalAttachment(Uri uri, String contentType, boolean allowMessageType) { addAttachment(uri, contentType, allowMessageType, true); } Loading @@ -174,25 +204,26 @@ public class AttachmentPresenter { addAttachmentAndStartLoader(attachment); } public boolean loadNonInlineAttachments(MessageViewInfo messageViewInfo) { public boolean loadAllAvailableAttachments(MessageViewInfo messageViewInfo) { boolean allPartsAvailable = true; for (AttachmentViewInfo attachmentViewInfo : messageViewInfo.attachments) { if (attachmentViewInfo.isContentAvailable()) { if (attachmentViewInfo.inlineAttachment) { continue; addInlineAttachment(attachmentViewInfo); } else { addInternalAttachment(attachmentViewInfo); } if (!attachmentViewInfo.isContentAvailable()) { } else { allPartsAvailable = false; continue; } addInternalAttachment(attachmentViewInfo); } return allPartsAvailable; } public void processMessageToForward(MessageViewInfo messageViewInfo) { boolean isMissingParts = !loadNonInlineAttachments(messageViewInfo); boolean isMissingParts = !loadAllAvailableAttachments(messageViewInfo); if (isMissingParts) { attachmentMvpView.showMissingAttachmentsPartialMessageWarning(); } Loading Loading @@ -313,6 +344,35 @@ public class AttachmentPresenter { } }; private LoaderManager.LoaderCallbacks<Attachment> mInlineAttachmentContentLoaderCallback = new LoaderManager.LoaderCallbacks<Attachment>() { @Override public Loader<Attachment> onCreateLoader(int id, Bundle args) { Uri uri = args.getParcelable(LOADER_ARG_ATTACHMENT); return new AttachmentContentLoader(context, inlineAttachments.get(uri).getAttachment()); } @Override public void onLoadFinished(Loader<Attachment> loader, Attachment attachment) { int loaderId = loader.getId(); loaderManager.destroyLoader(loaderId); if (attachment.state == Attachment.LoadingState.COMPLETE) { inlineAttachments.put(attachment.uri, new InlineAttachment( inlineAttachments.get(attachment.uri).getContentId(), attachment)); } else { inlineAttachments.remove(attachment.uri); } postPerformStalledAction(); } @Override public void onLoaderReset(Loader<Attachment> loader) { // nothing to do } }; private void postPerformStalledAction() { new Handler().post(new Runnable() { @Override Loading Loading
app/core/src/main/java/com/fsck/k9/message/MessageBuilder.java +38 −4 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ package com.fsck.k9.message; import java.util.Date; import java.util.List; import java.util.Map; import android.app.Activity; import android.app.PendingIntent; Loading Loading @@ -56,6 +57,7 @@ public abstract class MessageBuilder { private SimpleMessageFormat messageFormat; private String text; private List<Attachment> attachments; private Map<String, Attachment> inlineAttachments; private String signature; private QuoteStyle quoteStyle; private QuotedTextMode quotedTextMode; Loading Loading @@ -169,7 +171,16 @@ public abstract class MessageBuilder { // Let the receiver select either the text or the HTML part. bodyPlain = buildText(isDraft, SimpleMessageFormat.TEXT); composedMimeMessage.addBodyPart(MimeBodyPart.create(bodyPlain, "text/plain")); composedMimeMessage.addBodyPart(MimeBodyPart.create(body, "text/html")); MimeBodyPart htmlPart = MimeBodyPart.create(body, "text/html"); if (inlineAttachments != null && inlineAttachments.size() > 0) { MimeMultipart htmlPartWithInlineImages = new MimeMultipart("multipart/related", boundaryGenerator.generateBoundary()); htmlPartWithInlineImages.addBodyPart(htmlPart); addInlineAttachmentsToMessage(htmlPartWithInlineImages); composedMimeMessage.addBodyPart(MimeBodyPart.create(htmlPartWithInlineImages)); } else { composedMimeMessage.addBodyPart(htmlPart); } if (hasAttachments) { // If we're HTML and have attachments, we have a MimeMultipart container to hold the Loading Loading @@ -236,7 +247,25 @@ public abstract class MessageBuilder { MimeBodyPart bp = MimeBodyPart.create(body); addContentType(bp, attachment.getContentType(), attachment.getName()); addContentDisposition(bp, attachment.getName(), attachment.getSize()); addContentDisposition(bp, "attachment", attachment.getName(), attachment.getSize()); mp.addBodyPart(bp); } } private void addInlineAttachmentsToMessage(final MimeMultipart mp) throws MessagingException { for (String cid : inlineAttachments.keySet()) { Attachment attachment = inlineAttachments.get(cid); if (attachment.getState() != Attachment.LoadingState.COMPLETE) { continue; } Body body = new TempFileBody(attachment.getFileName()); MimeBodyPart bp = MimeBodyPart.create(body); addContentType(bp, attachment.getContentType(), attachment.getName()); addContentDisposition(bp, "inline", attachment.getName(), attachment.getSize()); bp.addHeader(MimeHeader.HEADER_CONTENT_ID, cid); mp.addBodyPart(bp); } Loading @@ -251,8 +280,8 @@ public abstract class MessageBuilder { } } private void addContentDisposition(MimeBodyPart bodyPart, String fileName, Long size) { String value = Headers.contentDisposition("attachment", fileName, size); private void addContentDisposition(MimeBodyPart bodyPart, String disposition, String fileName, Long size) { String value = Headers.contentDisposition(disposition, fileName, size); bodyPart.addHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, value); } Loading Loading @@ -401,6 +430,11 @@ public abstract class MessageBuilder { return this; } public MessageBuilder setInlineAttachments(Map<String, Attachment> attachments) { this.inlineAttachments = attachments; return this; } public MessageBuilder setSignature(String signature) { this.signature = signature; return this; Loading
app/core/src/test/java/com/fsck/k9/message/MessageBuilderTest.java +30 −0 Original line number Diff line number Diff line Loading @@ -9,7 +9,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import com.fsck.k9.Account.QuoteStyle; import com.fsck.k9.CoreResourceProvider; Loading @@ -23,6 +25,7 @@ import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.internet.MessageIdGenerator; import com.fsck.k9.mail.internet.MimeHeader; import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.message.MessageBuilder.Callback; Loading Loading @@ -275,6 +278,33 @@ public class MessageBuilderTest extends RobolectricTest { assertEquals("text/html", parts.get(1).getMimeType()); } @Test public void build_usingHtmlFormatWithInlineAttachment_shouldUseMultipartAlternativeInCorrectOrder() throws Exception { String contentId = "contentId"; String attachmentMimeType = "image/png"; Map<String, Attachment> inlineAttachments = new HashMap<>(); inlineAttachments.put(contentId, createAttachmentWithContent(attachmentMimeType, "1x1.png", "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC")); MessageBuilder messageBuilder = createHtmlMessageBuilder().setInlineAttachments(inlineAttachments); messageBuilder.buildAsync(callback); MimeMessage message = getMessageFromCallback(); assertEquals(MimeMultipart.class, message.getBody().getClass()); assertEquals("multipart/alternative", ((MimeMultipart) message.getBody()).getMimeType()); List<BodyPart> parts = ((MimeMultipart) message.getBody()).getBodyParts(); //RFC 2046 - 5.1.4. - Best type is last displayable assertEquals("text/plain", parts.get(0).getMimeType()); assertEquals("multipart/related", parts.get(1).getMimeType()); List<BodyPart> partWithInlineAttachment = ((MimeMultipart) parts.get(1).getBody()).getBodyParts(); assertEquals("text/html", partWithInlineAttachment.get(0).getMimeType()); assertEquals(attachmentMimeType, partWithInlineAttachment.get(1).getMimeType()); String[] attachmentHeaders = partWithInlineAttachment.get(1).getHeader(MimeHeader.HEADER_CONTENT_ID); assertEquals(1, attachmentHeaders.length); assertEquals(contentId, attachmentHeaders[0]); } @Test public void build_withMessageAttachment_shouldAttachAsMessageRfc822() throws Exception { MessageBuilder messageBuilder = createSimpleMessageBuilder(); Loading
app/ui/legacy/build.gradle +1 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ dependencies { testImplementation project(':app:storage') testImplementation project(':app:testing') testImplementation "org.robolectric:robolectric:${versions.robolectric}" testImplementation "androidx.test:core:${versions.androidxTestCore}" testImplementation "junit:junit:${versions.junit}" testImplementation "com.google.truth:truth:${versions.truth}" testImplementation "org.mockito:mockito-core:${versions.mockito}" Loading
app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageCompose.java +2 −1 Original line number Diff line number Diff line Loading @@ -713,6 +713,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, .setMessageFormat(currentMessageFormat) .setText(CrLfConverter.toCrLf(messageContentView.getText())) .setAttachments(attachmentPresenter.getAttachments()) .setInlineAttachments(attachmentPresenter.getInlineAttachments()) .setSignature(CrLfConverter.toCrLf(signatureView.getText())) .setSignatureBeforeQuotedText(account.isSignatureBeforeQuotedText()) .setIdentityChanged(identityChanged) Loading Loading @@ -1369,7 +1370,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, } if (!relatedMessageProcessed) { attachmentPresenter.loadNonInlineAttachments(messageViewInfo); attachmentPresenter.loadAllAvailableAttachments(messageViewInfo); } // Decode the identity header when loading a draft. Loading
app/ui/legacy/src/main/java/com/fsck/k9/activity/compose/AttachmentPresenter.java +69 −9 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ package com.fsck.k9.activity.compose; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import android.app.Activity; import android.content.ClipData; Loading @@ -19,6 +20,7 @@ import com.fsck.k9.activity.compose.ComposeCryptoStatus.AttachErrorState; import com.fsck.k9.activity.loader.AttachmentContentLoader; import com.fsck.k9.activity.loader.AttachmentInfoLoader; import com.fsck.k9.activity.misc.Attachment; import com.fsck.k9.activity.misc.InlineAttachment; import com.fsck.k9.controller.MessageReference; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mailstore.AttachmentViewInfo; Loading Loading @@ -46,7 +48,8 @@ public class AttachmentPresenter { private final AttachmentsChangedListener listener; // persistent state private LinkedHashMap<Uri, Attachment> attachments; private final LinkedHashMap<Uri, Attachment> attachments; private final LinkedHashMap<Uri, InlineAttachment> inlineAttachments; private int nextLoaderId = 0; private WaitingAction actionToPerformAfterWaiting = WaitingAction.NONE; Loading @@ -59,6 +62,7 @@ public class AttachmentPresenter { this.listener = listener; attachments = new LinkedHashMap<>(); inlineAttachments = new LinkedHashMap<>(); } public void onSaveInstanceState(Bundle outState) { Loading Loading @@ -122,6 +126,14 @@ public class AttachmentPresenter { return new ArrayList<>(attachments.values()); } public Map<String, com.fsck.k9.message.Attachment> getInlineAttachments() { Map<String, com.fsck.k9.message.Attachment> result = new LinkedHashMap<>(); for (InlineAttachment attachment : inlineAttachments.values()) { result.put(attachment.getContentId(), attachment.getAttachment()); } return result; } public void onClickAddAttachment(RecipientPresenter recipientPresenter) { ComposeCryptoStatus currentCachedCryptoStatus = recipientPresenter.getCurrentCachedCryptoStatus(); if (currentCachedCryptoStatus == null) { Loading Loading @@ -159,6 +171,24 @@ public class AttachmentPresenter { addAttachment(uri, contentType, false, false); } private void addInlineAttachment(AttachmentViewInfo attachmentViewInfo) { if (inlineAttachments.containsKey(attachmentViewInfo.internalUri)) { throw new IllegalStateException("Received the same attachmentViewInfo twice!"); } int loaderId = getNextFreeLoaderId(); Attachment attachment = Attachment.createAttachment( attachmentViewInfo.internalUri, loaderId, attachmentViewInfo.mimeType, true, true); attachment = attachment.deriveWithMetadataLoaded( attachmentViewInfo.mimeType, attachmentViewInfo.displayName, attachmentViewInfo.size); inlineAttachments.put(attachment.uri, new InlineAttachment(attachmentViewInfo.part.getContentId(), attachment)); Bundle bundle = new Bundle(); bundle.putParcelable(LOADER_ARG_ATTACHMENT, attachment.uri); loaderManager.initLoader(attachment.loaderId, bundle, mInlineAttachmentContentLoaderCallback); } private void addInternalAttachment(Uri uri, String contentType, boolean allowMessageType) { addAttachment(uri, contentType, allowMessageType, true); } Loading @@ -174,25 +204,26 @@ public class AttachmentPresenter { addAttachmentAndStartLoader(attachment); } public boolean loadNonInlineAttachments(MessageViewInfo messageViewInfo) { public boolean loadAllAvailableAttachments(MessageViewInfo messageViewInfo) { boolean allPartsAvailable = true; for (AttachmentViewInfo attachmentViewInfo : messageViewInfo.attachments) { if (attachmentViewInfo.isContentAvailable()) { if (attachmentViewInfo.inlineAttachment) { continue; addInlineAttachment(attachmentViewInfo); } else { addInternalAttachment(attachmentViewInfo); } if (!attachmentViewInfo.isContentAvailable()) { } else { allPartsAvailable = false; continue; } addInternalAttachment(attachmentViewInfo); } return allPartsAvailable; } public void processMessageToForward(MessageViewInfo messageViewInfo) { boolean isMissingParts = !loadNonInlineAttachments(messageViewInfo); boolean isMissingParts = !loadAllAvailableAttachments(messageViewInfo); if (isMissingParts) { attachmentMvpView.showMissingAttachmentsPartialMessageWarning(); } Loading Loading @@ -313,6 +344,35 @@ public class AttachmentPresenter { } }; private LoaderManager.LoaderCallbacks<Attachment> mInlineAttachmentContentLoaderCallback = new LoaderManager.LoaderCallbacks<Attachment>() { @Override public Loader<Attachment> onCreateLoader(int id, Bundle args) { Uri uri = args.getParcelable(LOADER_ARG_ATTACHMENT); return new AttachmentContentLoader(context, inlineAttachments.get(uri).getAttachment()); } @Override public void onLoadFinished(Loader<Attachment> loader, Attachment attachment) { int loaderId = loader.getId(); loaderManager.destroyLoader(loaderId); if (attachment.state == Attachment.LoadingState.COMPLETE) { inlineAttachments.put(attachment.uri, new InlineAttachment( inlineAttachments.get(attachment.uri).getContentId(), attachment)); } else { inlineAttachments.remove(attachment.uri); } postPerformStalledAction(); } @Override public void onLoaderReset(Loader<Attachment> loader) { // nothing to do } }; private void postPerformStalledAction() { new Handler().post(new Runnable() { @Override Loading