LegacyConversions.java revision 936babc145e2e6eb2e222f2ce5e3da8f9e4fb9f2
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.email; 18 19import com.android.email.mail.Address; 20import com.android.email.mail.Flag; 21import com.android.email.mail.Message; 22import com.android.email.mail.MessagingException; 23import com.android.email.mail.Part; 24import com.android.email.mail.internet.MimeHeader; 25import com.android.email.mail.internet.MimeUtility; 26import com.android.email.provider.AttachmentProvider; 27import com.android.email.provider.EmailContent; 28import com.android.email.provider.EmailContent.Attachment; 29import com.android.email.provider.EmailContent.AttachmentColumns; 30 31import org.apache.commons.io.IOUtils; 32 33import android.content.ContentUris; 34import android.content.ContentValues; 35import android.content.Context; 36import android.net.Uri; 37 38import java.io.File; 39import java.io.FileOutputStream; 40import java.io.IOException; 41import java.io.InputStream; 42import java.util.ArrayList; 43import java.util.Date; 44 45public class LegacyConversions { 46 47 /** 48 * Copy field-by-field from a "store" message to a "provider" message 49 * @param message The message we've just downloaded 50 * @param localMessage The message we'd like to write into the DB 51 * @result true if dirty (changes were made) 52 */ 53 public static boolean updateMessageFields(EmailContent.Message localMessage, Message message, 54 long accountId, long mailboxId) throws MessagingException { 55 56 Address[] from = message.getFrom(); 57 Address[] to = message.getRecipients(Message.RecipientType.TO); 58 Address[] cc = message.getRecipients(Message.RecipientType.CC); 59 Address[] bcc = message.getRecipients(Message.RecipientType.BCC); 60 Address[] replyTo = message.getReplyTo(); 61 String subject = message.getSubject(); 62 Date sentDate = message.getSentDate(); 63 64 if (from != null && from.length > 0) { 65 localMessage.mDisplayName = from[0].toFriendly(); 66 } 67 if (sentDate != null) { 68 localMessage.mTimeStamp = sentDate.getTime(); 69 } 70 if (subject != null) { 71 localMessage.mSubject = subject; 72 } 73 localMessage.mFlagRead = message.isSet(Flag.SEEN); 74 75 // Keep the message in the "unloaded" state until it has (at least) a display name. 76 // This prevents early flickering of empty messages in POP download. 77 if (localMessage.mFlagLoaded != EmailContent.Message.LOADED) { 78 if (localMessage.mDisplayName == null || "".equals(localMessage.mDisplayName)) { 79 localMessage.mFlagLoaded = EmailContent.Message.NOT_LOADED; 80 } else { 81 localMessage.mFlagLoaded = EmailContent.Message.PARTIALLY_LOADED; 82 } 83 } 84 localMessage.mFlagFavorite = message.isSet(Flag.FLAGGED); 85// public boolean mFlagAttachment = false; 86// public int mFlags = 0; 87 88 localMessage.mServerId = message.getUid(); 89// public int mServerIntId; 90// public String mClientId; 91// public String mMessageId; 92 93// public long mBodyKey; 94 localMessage.mMailboxKey = mailboxId; 95 localMessage.mAccountKey = accountId; 96 97 if (from != null && from.length > 0) { 98 localMessage.mFrom = Address.pack(from); 99 } 100 101 localMessage.mTo = Address.pack(to); 102 localMessage.mCc = Address.pack(cc); 103 localMessage.mBcc = Address.pack(bcc); 104 localMessage.mReplyTo = Address.pack(replyTo); 105 106// public String mText; 107// public String mHtml; 108// public String mTextReply; 109// public String mHtmlReply; 110 111// // Can be used while building messages, but is NOT saved by the Provider 112// transient public ArrayList<Attachment> mAttachments = null; 113 114 return true; 115 } 116 117 /** 118 * Copy body text (plain and/or HTML) from MimeMessage to provider Message 119 */ 120 public static boolean updateBodyFields(EmailContent.Body body, 121 EmailContent.Message localMessage, ArrayList<Part> viewables) 122 throws MessagingException { 123 124 body.mMessageKey = localMessage.mId; 125 126 StringBuffer sbHtml = new StringBuffer(); 127 StringBuffer sbText = new StringBuffer(); 128 for (Part viewable : viewables) { 129 String text = MimeUtility.getTextFromPart(viewable); 130 if ("text/html".equalsIgnoreCase(viewable.getMimeType())) { 131 if (sbHtml.length() > 0) { 132 sbHtml.append('\n'); 133 } 134 sbHtml.append(text); 135 } else { 136 if (sbText.length() > 0) { 137 sbText.append('\n'); 138 } 139 sbText.append(text); 140 } 141 } 142 143 // write the combined data to the body part 144 if (sbText.length() != 0) { 145 body.mTextContent = sbText.toString(); 146 } 147 if (sbHtml.length() != 0) { 148 body.mHtmlContent = sbHtml.toString(); 149 } 150 return true; 151 } 152 153 /** 154 * Copy attachments from MimeMessage to provider Message. 155 * 156 * @param context a context for file operations 157 * @param localMessage the attachments will be built against this message 158 * @param attachments the attachments to add 159 * @throws IOException 160 */ 161 public static void updateAttachments(Context context, EmailContent.Message localMessage, 162 ArrayList<Part> attachments) throws MessagingException, IOException { 163 localMessage.mAttachments = null; 164 for (Part attachmentPart : attachments) { 165 addOneAttachment(context, localMessage, attachmentPart); 166 } 167 } 168 169 /** 170 * Add a single attachment part to the message 171 * 172 * TODO: This will simply add to any existing attachments - could this ever happen? If so, 173 * change it to find existing attachments and delete/merge them. 174 * TODO: Take a closer look at encoding and deal with it if necessary. 175 * 176 * @param context a context for file operations 177 * @param localMessage the attachments will be built against this message 178 * @param part a single attachment part from POP or IMAP 179 * @throws IOException 180 */ 181 private static void addOneAttachment(Context context, EmailContent.Message localMessage, 182 Part part) throws MessagingException, IOException { 183 184 Attachment localAttachment = new Attachment(); 185 186 // Transfer fields from mime format to provider format 187 String contentType = MimeUtility.unfoldAndDecode(part.getContentType()); 188 String name = MimeUtility.getHeaderParameter(contentType, "name"); 189 if (name == null) { 190 String contentDisposition = MimeUtility.unfoldAndDecode(part.getContentType()); 191 name = MimeUtility.getHeaderParameter(contentDisposition, "filename"); 192 } 193 194 // Try to pull size from disposition (if not downloaded) 195 long size = 0; 196 String disposition = part.getDisposition(); 197 if (disposition != null) { 198 String s = MimeUtility.getHeaderParameter(disposition, "size"); 199 if (s != null) { 200 size = Long.parseLong(s); 201 } 202 } 203 204 // Get partId for unloaded IMAP attachments (if any) 205 // This is only provided (and used) when we have structure but not the actual attachment 206 String[] partIds = part.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA); 207 String partId = partIds != null ? partIds[0] : null; 208 209 localAttachment.mFileName = MimeUtility.getHeaderParameter(contentType, "name"); 210 localAttachment.mMimeType = part.getMimeType(); 211 localAttachment.mSize = size; // May be reset below if file handled 212 localAttachment.mContentId = part.getContentId(); 213 localAttachment.mContentUri = null; // Will be set when file is saved 214 localAttachment.mMessageKey = localMessage.mId; 215 localAttachment.mLocation = partId; 216 localAttachment.mEncoding = "B"; // TODO - convert other known encodings 217 218 // Save the attachment (so far) in order to obtain an id 219 localAttachment.save(context); 220 221 // If an attachment body was actually provided, we need to write the file now 222 saveAttachmentBody(context, part, localAttachment, localMessage.mAccountKey); 223 224 if (localMessage.mAttachments == null) { 225 localMessage.mAttachments = new ArrayList<Attachment>(); 226 } 227 localMessage.mAttachments.add(localAttachment); 228 localMessage.mFlagAttachment = true; 229 } 230 231 /** 232 * Save the body part of a single attachment, to a file in the attachments directory. 233 */ 234 public static void saveAttachmentBody(Context context, Part part, Attachment localAttachment, 235 long accountId) throws MessagingException, IOException { 236 if (part.getBody() != null) { 237 long attachmentId = localAttachment.mId; 238 239 InputStream in = part.getBody().getInputStream(); 240 241 File saveIn = AttachmentProvider.getAttachmentDirectory(context, accountId); 242 if (!saveIn.exists()) { 243 saveIn.mkdirs(); 244 } 245 File saveAs = AttachmentProvider.getAttachmentFilename(context, accountId, 246 attachmentId); 247 saveAs.createNewFile(); 248 FileOutputStream out = new FileOutputStream(saveAs); 249 long copySize = IOUtils.copy(in, out); 250 in.close(); 251 out.close(); 252 253 // update the attachment with the extra information we now know 254 String contentUriString = AttachmentProvider.getAttachmentUri( 255 accountId, attachmentId).toString(); 256 257 localAttachment.mSize = copySize; 258 localAttachment.mContentUri = contentUriString; 259 260 // update the attachment in the database as well 261 ContentValues cv = new ContentValues(); 262 cv.put(AttachmentColumns.SIZE, copySize); 263 cv.put(AttachmentColumns.CONTENT_URI, contentUriString); 264 Uri uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, attachmentId); 265 context.getContentResolver().update(uri, cv, null, null); 266 } 267 } 268 269} 270