public async Task OpenAttachmentAsync(MailMessage message, Attachment attachment) { // Lazy load if (attachment.Scope < Scope.HeadersAndBody) { // Check local storage if (MailStorage.HasMessagePart(message.GetThreadId(), message.GetMessageId(), attachment.BodyId)) { // TODO: Can we open the attachment directly from isolated storage? attachment.Body = await MailStorage.GetMessagePartAsync(message.GetThreadId(), message.GetMessageId(), attachment.BodyId); } else { // Download from the network await GmailImap.GetBodyPartAsync(message.Uid, attachment, async() => { if (ActiveLabel.Info.StoreMessages && ActiveLabel.Info.StoreAttachments) { await MailStorage.StoreMessagePartAsync(message.GetThreadId(), message.GetMessageId(), attachment.BodyId, attachment.Body); } }, CancellationToken.None); } } StorageFile file = await MailStorage.SaveAttachmentToTempAsync(attachment); // http://architects.dzone.com/articles/lap-around-windows-phone-8-sdk await Launcher.LaunchFileAsync(file); // TODO: Delete temp files on app close? }
public virtual async Task LazyLoadBodyPartAsync(MailMessage message, ObjectWHeaders part) { if (part.Scope < Scope.HeadersAndBody) { if (MailStorage.HasMessagePart(message.GetThreadId(), message.GetMessageId(), part.BodyId)) { part.Body = await MailStorage.GetMessagePartAsync(message.GetThreadId(), message.GetMessageId(), part.BodyId); } else { await GmailImap.GetBodyPartAsync(message.Uid, part, async() => { if (ActiveLabel.Info.StoreMessages) { await MailStorage.StoreMessagePartAsync(message.GetThreadId(), message.GetMessageId(), part.BodyId, part.Body); } }, CancellationToken.None); } } }
// Examine the local data store to see if there are any attachment bodies that still need to be downloaded. public async Task SyncAttachmentsAsync(CancellationToken cancellationToken) { List <MessageIdInfo> localIds = await MailStorage.GetLabelMessageListAsync(ActiveLabel.Info.Name) ?? new List <MessageIdInfo>(); List <KeyValuePair <MessageIdInfo, ObjectWHeaders> > attachmentsToDownload = new List <KeyValuePair <MessageIdInfo, ObjectWHeaders> >(); foreach (MessageIdInfo ids in localIds) { if (cancellationToken.IsCancellationRequested) { return; } MailMessage headers = await MailStorage.GetMessageHeadersAsync(ids.ThreadId, ids.MessageId); if (headers == null) { // Downloading headers should have happened elsewhere. continue; } if (headers.HasMutipartBody) { foreach (Attachment attachment in headers.Attachments) { if (!MailStorage.HasMessagePart(ids.ThreadId, ids.MessageId, attachment.BodyId)) { attachmentsToDownload.Add(new KeyValuePair <MessageIdInfo, ObjectWHeaders>(ids, attachment)); } } } } if (cancellationToken.IsCancellationRequested) { return; } // TODO: Batch by bodyId? foreach (var pair in attachmentsToDownload) { if (cancellationToken.IsCancellationRequested) { return; } // TODO: Consider streaming the body directly to disk. Even more so for attachments. await GmailImap.GetBodyPartAsync(pair.Key.Uid, pair.Value, async() => { if (cancellationToken.IsCancellationRequested) { return; } await MailStorage.StoreMessagePartAsync(pair.Key.ThreadId, pair.Key.MessageId, pair.Value.BodyId, pair.Value.Body); // Release the body for GC. Otherwise the bodiesToDownload list will keep everything im memory. pair.Value.Body = null; }, cancellationToken); } }
// Examine the local data store to see if there are any message bodies that still need to be downloaded. public async Task SyncMessageBodiesAsync(CancellationToken cancellationToken) { List <MessageIdInfo> localIds = await MailStorage.GetLabelMessageListAsync(ActiveLabel.Info.Name) ?? new List <MessageIdInfo>(); List <KeyValuePair <MessageIdInfo, ObjectWHeaders> > bodiesToDownload = new List <KeyValuePair <MessageIdInfo, ObjectWHeaders> >(); foreach (MessageIdInfo ids in localIds) { if (cancellationToken.IsCancellationRequested) { return; } MailMessage headers = await MailStorage.GetMessageHeadersAsync(ids.ThreadId, ids.MessageId); if (headers == null) { // Downloading headers should have happened elsewhere. continue; } if (headers.HasMutipartBody) { foreach (Attachment view in headers.AlternateViews) { if (!MailStorage.HasMessagePart(ids.ThreadId, ids.MessageId, view.BodyId)) { bodiesToDownload.Add(new KeyValuePair <MessageIdInfo, ObjectWHeaders>(ids, view)); } } // Attachments will be downloaded seperately as well. // TODO: but maybe we could build the list here while we're looking? } else { // Primary body, no attachments or alternate views if (!MailStorage.HasMessagePart(ids.ThreadId, ids.MessageId, headers.BodyId)) { bodiesToDownload.Add(new KeyValuePair <MessageIdInfo, ObjectWHeaders>(ids, headers)); } } } if (cancellationToken.IsCancellationRequested) { return; } // TODO: Batch foreach (var pair in bodiesToDownload) { if (cancellationToken.IsCancellationRequested) { return; } // TODO: Consider only downloading the first X bytes of each message, and loading more only on demand. // TODO: Consider streaming the body directly to disk. Even more so for attachments. await GmailImap.GetBodyPartAsync(pair.Key.Uid, pair.Value, async() => { if (cancellationToken.IsCancellationRequested) { return; } await MailStorage.StoreMessagePartAsync(pair.Key.ThreadId, pair.Key.MessageId, pair.Value.BodyId, pair.Value.Body); // Release the body for GC. Otherwise the bodiesToDownload list will keep everything im memory. pair.Value.Body = null; }, cancellationToken); } }