package com.kescoode.xmail.domain; import android.content.Context; import android.database.Cursor; import android.text.TextUtils; import com.fsck.k9.mail.*; import com.fsck.k9.mail.internet.*; import com.kescoode.xmail.controller.MailManager; import com.kescoode.xmail.domain.internal.HtmlConverter; import com.kescoode.xmail.domain.internal.UiMessageContent; import com.kescoode.xmail.domain.internal.UiMessageExtractor; import com.kescoode.xmail.exception.XDynamicException; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.util.Date; import java.util.Locale; /** * 本地邮件业务对象 * * @author Kesco Lin */ public class LocalEmail extends MimeMessage { private final Context context; private volatile long id = -1; private boolean isRead = false; private boolean isFlagged = false; private boolean isForward = false; private String textPath; private String htmlPath; private String preview; private String subject; private int status = -1; private String text = null; private String html = null; public LocalEmail(Context context, LocalFolder folder, Cursor cursor) { this.context = context; this.mFolder = folder; this.id = cursor.getLong(0); this.mUid = cursor.getString(1); this.mFrom = Address.unpack(cursor.getString(3)); this.mTo = Address.unpack(cursor.getString(4)); this.mCc = Address.unpack(cursor.getString(5)); this.mBcc = Address.unpack(cursor.getString(6)); this.isRead = cursor.getInt(7) == 1; this.isFlagged = cursor.getInt(8) == 1; this.isForward = cursor.getInt(9) == 1; this.textPath = cursor.getString(10); this.htmlPath = cursor.getString(11); this.preview = cursor.getString(12); this.subject = cursor.getString(13); setInternalDate(new Date(cursor.getLong(14))); this.status = cursor.getInt(15); // TODO: 把读取本地文件放后 try { this.text = FileUtils.readFileToString(new File(this.textPath)); this.html = FileUtils.readFileToString(new File(this.htmlPath)); } catch (IOException e) { throw new XDynamicException("Cannot load Local Text"); } try { String mimeType = getMimeType(); MimeMultipart mp = new MimeMultipart(); mp.setSubType("mixed"); if (mimeType != null && mimeType.toLowerCase(Locale.US).startsWith("multipart/")) { // If this is a multipart message, preserve both text // and html parts, as well as the subtype. mp.setSubType(mimeType.toLowerCase(Locale.US).replaceFirst("^multipart/", "")); if (text != null) { LocalTextBody body = new LocalTextBody(text, html); MimeBodyPart bp = new MimeBodyPart(body, "text/plain"); mp.addBodyPart(bp); } if (html != null) { TextBody body = new TextBody(html); MimeBodyPart bp = new MimeBodyPart(body, "text/html"); mp.addBodyPart(bp); } // If we have both text and html content and our MIME type // isn't multipart/alternative, then corral them into a new // multipart/alternative part and put that into the parent. // If it turns out that this is the only part in the parent // MimeMultipart, it'll get fixed below before we attach to // the message. if (text != null && html != null && !mimeType.equalsIgnoreCase("multipart/alternative")) { MimeMultipart alternativeParts = mp; alternativeParts.setSubType("alternative"); mp = new MimeMultipart(); mp.addBodyPart(new MimeBodyPart(alternativeParts)); } } else if (mimeType != null && mimeType.equalsIgnoreCase("text/plain")) { // If it's text, add only the plain part. The MIME // container will drop away below. if (text != null) { LocalTextBody body = new LocalTextBody(text, html); MimeBodyPart bp = new MimeBodyPart(body, "text/plain"); mp.addBodyPart(bp); } } else if (mimeType != null && mimeType.equalsIgnoreCase("text/html")) { // If it's html, add only the html part. The MIME // container will drop away below. if (html != null) { TextBody body = new TextBody(html); MimeBodyPart bp = new MimeBodyPart(body, "text/html"); mp.addBodyPart(bp); } } else { // MIME type not set. Grab whatever part we can get, // with Text taking precedence. This preserves pre-HTML // composition behaviour. if (text != null) { LocalTextBody body = new LocalTextBody(text, html); MimeBodyPart bp = new MimeBodyPart(body, "text/plain"); mp.addBodyPart(bp); } else if (html != null) { TextBody body = new TextBody(html); MimeBodyPart bp = new MimeBodyPart(body, "text/html"); mp.addBodyPart(bp); } } if (mp.getCount() == 0) { // If we have no body, remove the container and create a // dummy plain text body. This check helps prevents us from // triggering T_MIME_NO_TEXT and T_TVD_MIME_NO_HEADERS // SpamAssassin rules. setHeader(MimeHeader.HEADER_CONTENT_TYPE, "text/plain"); MimeMessageHelper.setBody(this, new TextBody("")); } else if (mp.getCount() == 1 && !(mp.getBodyPart(0) instanceof LocalAttachmentBodyPart)) { // If we have only one part, drop the MimeMultipart container. BodyPart part = mp.getBodyPart(0); setHeader(MimeHeader.HEADER_CONTENT_TYPE, part.getContentType()); MimeMessageHelper.setBody(this, part.getBody()); } else { // Otherwise, attach the MimeMultipart to the message. MimeMessageHelper.setBody(this, mp); } } catch (MessagingException e) { } } public LocalEmail(Context context, LocalFolder folder, Message remote) { this.context = context; this.mFolder = folder; this.mUid = remote.getUid(); this.mFrom = remote.getFrom(); try { this.mTo = remote.getRecipients(RecipientType.TO); this.mCc = remote.getRecipients(RecipientType.CC); this.mBcc = remote.getRecipients(RecipientType.BCC); // TODO: 这里和K9现在的实现不一样,以后要做相应的改变 UiMessageContent content = UiMessageExtractor .extractMessageFromScratch(context, remote); this.text = content.text; this.html = HtmlConverter.convertEmoji2Img(content.html); // TODO: 把这里的工作放在保存数据库之后 File file = generateFilePath(false); this.textPath = file.getPath(); FileUtils.writeStringToFile(file, this.text); file = generateFilePath(true); FileUtils.writeStringToFile(file, this.html); this.htmlPath = file.getPath(); this.preview = content.calculateContentPreview(content.text); setInternalDate(remote.getSentDate() != null ? remote.getSentDate() : remote.getInternalDate()); } catch (MessagingException e) { throw new RuntimeException("Can not load remote message"); } catch (IOException e) { throw new XDynamicException("Can not load remote message"); } this.subject = remote.getSubject(); } public String getTextForDisplay() throws MessagingException { String text = null; // First try and fetch an HTML part. Part part = MimeUtility.findFirstPartByMimeType(this, "text/html"); if (part == null) { // If that fails, try and get a text part. part = MimeUtility.findFirstPartByMimeType(this, "text/plain"); if (part != null && part.getBody() instanceof LocalTextBody) { text = ((LocalTextBody) part.getBody()).getBodyForDisplay(); } } else { // We successfully found an HTML part; do the necessary character set decoding. text = MessageExtractor.getTextFromPart(part); } return text; } @Override public String getMimeType() { // TODO: 我觉得这样是非常不合理的做法,应该在数据库上做mime的记录,待项目进展把它重构掉 if (TextUtils.isEmpty(textPath)) { return "text/html"; } else if (TextUtils.isEmpty(htmlPath)) { return "text/plain"; } else { return "multipart/alternative"; } } @Override public LocalFolder getFolder() { return (LocalFolder) mFolder; } @Override public String getSubject() { return subject != null ? subject : ""; } @Override public String getPreview() { return preview; } /** * 这里把Send Date和Internal Date统一了 * * @return 发送日期 */ @Override public Date getSentDate() { return getInternalDate(); } @Override public void setInternalDate(Date internalDate) { super.setInternalDate(internalDate != null ? internalDate : new Date()); } public long getFolderId() { return getFolder().getId(); } public long getId() { if (id == -1) { throw new UnsupportedOperationException("The mail has not been insert into database"); } return id; } public void setId(long id) { this.id = id; } public boolean isRead() { return isRead; } public void setRead(boolean isRead) { this.isRead = isRead; } public boolean isFlagged() { return isFlagged; } public void setFlagged(boolean isFlagged) { this.isFlagged = isFlagged; } public boolean isForward() { return isForward; } public void setForward(boolean isForward) { this.isForward = isForward; } public String getHtmlPath() { return htmlPath; } public String getTextPath() { return textPath; } public int getStatus() { return status; } /** * 创建内容Path(随机) * * @param html 是否为html文档 * @return 路径的File对象 */ private File generateFilePath(boolean html) { String tmp = System.currentTimeMillis() + "-" + hashCode(); String path; if (html) { path = "html-" + tmp; } else { path = "text-" + tmp; } MailManager manager = MailManager.getSingleTon(context); return new File(manager.getContentDir(), path); } }