package com.kescoode.xmail.domain.internal; import android.content.Context; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MessageExtractor; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.Viewable; import com.kescoode.xmail.R; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * 把{@link com.kescoode.xmail.domain.LocalEmail}映射到</p> * {@link UiMessageContent}上 * * @author Jinsen Lin */ public class UiMessageExtractor { private static final String TEXT_DIVIDER = "------------------------------------------------------------------------"; private static final int TEXT_DIVIDER_LENGTH = TEXT_DIVIDER.length(); private static final String FILENAME_PREFIX = "----- "; private static final int FILENAME_PREFIX_LENGTH = FILENAME_PREFIX.length(); private static final String FILENAME_SUFFIX = " "; private static final int FILENAME_SUFFIX_LENGTH = FILENAME_SUFFIX.length(); private UiMessageExtractor() { } public static UiMessageContent extractMessageFromScratch(Context ctx, Message message) throws MessagingException { try { List<Part> attachments = new ArrayList<Part>(); // Collect all viewable parts List<Viewable> viewables = MessageExtractor.getViewables(message, attachments); /* * Convert the tree of viewable parts into text and HTML */ // Used to suppress the divider for the first viewable part boolean hideDivider = true; StringBuilder text = new StringBuilder(); StringBuilder html = new StringBuilder(); for (Viewable viewable : viewables) { if (viewable instanceof Viewable.Textual) { // This is either a text/plain or text/html part. Fill the variables 'text' and // 'html', converting between plain text and HTML as necessary. text.append(buildText(viewable, !hideDivider)); html.append(buildHtml(viewable, !hideDivider)); hideDivider = false; } else if (viewable instanceof Viewable.MessageHeader) { Viewable.MessageHeader header = (Viewable.MessageHeader) viewable; Part containerPart = header.getContainerPart(); Message innerMessage = header.getMessage(); addTextDivider(text, containerPart, !hideDivider); addMessageHeaderText(ctx, text, innerMessage); addHtmlDivider(html, containerPart, !hideDivider); addMessageHeaderHtml(ctx, html, innerMessage); hideDivider = true; } else if (viewable instanceof Viewable.Alternative) { // Handle multipart/alternative contents Viewable.Alternative alternative = (Viewable.Alternative) viewable; /* * We made sure at least one of text/plain or text/html is present when * creating the Alternative object. If one part is not present we convert the * other one to make sure 'text' and 'html' always contain the same text. */ List<Viewable> textAlternative = alternative.getText().isEmpty() ? alternative.getHtml() : alternative.getText(); List<Viewable> htmlAlternative = alternative.getHtml().isEmpty() ? alternative.getText() : alternative.getHtml(); // Fill the 'text' variable boolean divider = !hideDivider; for (Viewable textViewable : textAlternative) { text.append(buildText(textViewable, divider)); divider = true; } // Fill the 'html' variable divider = !hideDivider; for (Viewable htmlViewable : htmlAlternative) { html.append(buildHtml(htmlViewable, divider)); divider = true; } hideDivider = false; } } return new UiMessageContent(text.toString(), html.toString(), attachments); } catch (Exception e) { throw new MessagingException("Couldn't extract viewable parts", e); } } private static StringBuilder buildText(Viewable viewable, boolean prependDivider) { StringBuilder text = new StringBuilder(); if (viewable instanceof Viewable.Textual) { Part part = ((Viewable.Textual) viewable).getPart(); addTextDivider(text, part, prependDivider); String t = MessageExtractor.getTextFromPart(part); if (t == null) { t = ""; } else if (viewable instanceof Viewable.Html) { t = HtmlConverter.htmlToText(t); } text.append(t); } else if (viewable instanceof Viewable.Alternative) { // That's odd - an Alternative as child of an Alternative; go ahead and try to use the // text/plain child; fall-back to the text/html part. Viewable.Alternative alternative = (Viewable.Alternative) viewable; List<Viewable> textAlternative = alternative.getText().isEmpty() ? alternative.getHtml() : alternative.getText(); boolean divider = prependDivider; for (Viewable textViewable : textAlternative) { text.append(buildText(textViewable, divider)); divider = true; } } return text; } private static StringBuilder buildHtml(Viewable viewable, boolean prependDivider) { StringBuilder html = new StringBuilder(); if (viewable instanceof Viewable.Textual) { Part part = ((Viewable.Textual) viewable).getPart(); addHtmlDivider(html, part, prependDivider); String t = MessageExtractor.getTextFromPart(part); if (t == null) { t = ""; } else if (viewable instanceof Viewable.Text) { t = HtmlConverter.textToHtml(t); } html.append(t); } else if (viewable instanceof Viewable.Alternative) { // That's odd - an Alternative as child of an Alternative; go ahead and try to use the // text/html child; fall-back to the text/plain part. Viewable.Alternative alternative = (Viewable.Alternative) viewable; List<Viewable> htmlAlternative = alternative.getHtml().isEmpty() ? alternative.getText() : alternative.getHtml(); boolean divider = prependDivider; for (Viewable htmlViewable : htmlAlternative) { html.append(buildHtml(htmlViewable, divider)); divider = true; } } return html; } private static void addMessageHeaderText(Context context, StringBuilder text, Message message) throws MessagingException { // From: <sender> Address[] from = message.getFrom(); if (from != null && from.length > 0) { text.append(context.getString(R.string.message_compose_quote_header_from)); text.append(' '); text.append(Address.toString(from)); text.append("\r\n"); } // To: <recipients> Address[] to = message.getRecipients(Message.RecipientType.TO); if (to != null && to.length > 0) { text.append(context.getString(R.string.message_compose_quote_header_to)); text.append(' '); text.append(Address.toString(to)); text.append("\r\n"); } // Cc: <recipients> Address[] cc = message.getRecipients(Message.RecipientType.CC); if (cc != null && cc.length > 0) { text.append(context.getString(R.string.message_compose_quote_header_cc)); text.append(' '); text.append(Address.toString(cc)); text.append("\r\n"); } // Date: <date> Date date = message.getSentDate(); if (date != null) { text.append(context.getString(R.string.message_compose_quote_header_send_date)); text.append(' '); text.append(date.toString()); text.append("\r\n"); } // Subject: <subject> String subject = message.getSubject(); text.append(context.getString(R.string.message_compose_quote_header_subject)); text.append(' '); if (subject == null) { text.append(context.getString(R.string.general_no_subject)); } else { text.append(subject); } text.append("\r\n\r\n"); } private static void addMessageHeaderHtml(Context context, StringBuilder html, Message message) throws MessagingException { html.append("<table style=\"border: 0\">"); // From: <sender> Address[] from = message.getFrom(); if (from != null && from.length > 0) { addTableRow(html, context.getString(R.string.message_compose_quote_header_from), Address.toString(from)); } // To: <recipients> Address[] to = message.getRecipients(Message.RecipientType.TO); if (to != null && to.length > 0) { addTableRow(html, context.getString(R.string.message_compose_quote_header_to), Address.toString(to)); } // Cc: <recipients> Address[] cc = message.getRecipients(Message.RecipientType.CC); if (cc != null && cc.length > 0) { addTableRow(html, context.getString(R.string.message_compose_quote_header_cc), Address.toString(cc)); } // Date: <date> Date date = message.getSentDate(); if (date != null) { addTableRow(html, context.getString(R.string.message_compose_quote_header_send_date), date.toString()); } // Subject: <subject> String subject = message.getSubject(); addTableRow(html, context.getString(R.string.message_compose_quote_header_subject), (subject == null) ? context.getString(R.string.general_no_subject) : subject); html.append("</table>"); } private static void addTableRow(StringBuilder html, String header, String value) { html.append("<tr><th style=\"text-align: left; vertical-align: top;\">"); html.append(header); html.append("</th>"); html.append("<td>"); html.append(value); html.append("</td></tr>"); } private static void addHtmlDivider(StringBuilder html, Part part, boolean prependDivider) { if (prependDivider) { String filename = getPartName(part); html.append("<p style=\"margin-top: 2.5em; margin-bottom: 1em; border-bottom: 1px solid #000\">"); html.append(filename); html.append("</p>"); } } private static void addTextDivider(StringBuilder text, Part part, boolean prependDivider) { if (prependDivider) { String filename = getPartName(part); text.append("\r\n\r\n"); int len = filename.length(); if (len > 0) { if (len > TEXT_DIVIDER_LENGTH - FILENAME_PREFIX_LENGTH - FILENAME_SUFFIX_LENGTH) { filename = filename.substring(0, TEXT_DIVIDER_LENGTH - FILENAME_PREFIX_LENGTH - FILENAME_SUFFIX_LENGTH - 3) + "..."; } text.append(FILENAME_PREFIX); text.append(filename); text.append(FILENAME_SUFFIX); text.append(TEXT_DIVIDER.substring(0, TEXT_DIVIDER_LENGTH - FILENAME_PREFIX_LENGTH - filename.length() - FILENAME_SUFFIX_LENGTH)); } else { text.append(TEXT_DIVIDER); } text.append("\r\n\r\n"); } } private static String getPartName(Part part) { try { String disposition = part.getDisposition(); if (disposition != null) { String name = MimeUtility.getHeaderParameter(disposition, "filename"); return (name == null) ? "" : name; } } catch (MessagingException e) { /* ignore */ } return ""; } }