diff --git a/src/com/fsck/k9/helper/HtmlToTextTagHandler.java b/src/com/fsck/k9/helper/HtmlToTextTagHandler.java new file mode 100644 index 000000000..725bbc5a0 --- /dev/null +++ b/src/com/fsck/k9/helper/HtmlToTextTagHandler.java @@ -0,0 +1,85 @@ +package com.fsck.k9.helper; + +import android.text.Annotation; +import android.text.Editable; +import android.text.Html; +import android.text.Spannable; +import org.xml.sax.XMLReader; + +/** + * Custom tag handler to use when converting HTML messages to text. It currently handles text + * representations of HTML tags that Android's built-in parser doesn't understand and hides code + * contained in STYLE and SCRIPT blocks. + */ +public class HtmlToTextTagHandler implements Html.TagHandler +{ + @Override + public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) + { + if (tag.equalsIgnoreCase("hr") && opening) + { + // In the case of an
, replace it with a bunch of underscores. This is roughly the behaviour of + // Outlook in Rich Text mode. + output.append("_____________________________________________\n"); + } + else if (tag.equalsIgnoreCase("style") || tag.equalsIgnoreCase("script")) + { + handleIgnoredTag(opening, output); + } + } + + private static final String IGNORED_ANNOTATION_KEY = "K9_ANNOTATION"; + private static final String IGNORED_ANNOTATION_VALUE = "hiddenSpan"; + + /** + * When we come upon an ignored tag, we mark it with an Annotation object with a specific key + * and value as above. We don't really need to be checking these values since Html.fromHtml() + * doesn't use Annotation spans, but we should do it now to be safe in case they do start using + * it in the future. + * @param opening If this is an opening tag or not. + * @param output Spannable string that we're working with. + */ + private void handleIgnoredTag(boolean opening, Editable output) + { + int len = output.length(); + if (opening) + { + output.setSpan(new Annotation(IGNORED_ANNOTATION_KEY, IGNORED_ANNOTATION_VALUE), len, + len, Spannable.SPAN_MARK_MARK); + } + else + { + Object start = getOpeningAnnotation(output); + if (start != null) + { + int where = output.getSpanStart(start); + // Remove the temporary Annotation span. + output.removeSpan(start); + // Delete everything between the start of the Annotation and the end of the string + // (what we've generated so far). + output.delete(where, len); + } + } + } + + /** + * Fetch the matching opening Annotation object and verify that it's the one added by K9. + * @param output Spannable string we're working with. + * @return Starting Annotation object. + */ + private Object getOpeningAnnotation(Editable output) + { + Object[] objs = output.getSpans(0, output.length(), Annotation.class); + for (int i = objs.length - 1; i >= 0; i--) + { + Annotation span = (Annotation) objs[i]; + if (output.getSpanFlags(objs[i]) == Spannable.SPAN_MARK_MARK + && span.getKey().equals(IGNORED_ANNOTATION_KEY) + && span.getValue().equals(IGNORED_ANNOTATION_VALUE)) + { + return objs[i]; + } + } + return null; + } +} diff --git a/src/com/fsck/k9/mail/store/LocalStore.java b/src/com/fsck/k9/mail/store/LocalStore.java index e1127d588..fe8cf4c41 100644 --- a/src/com/fsck/k9/mail/store/LocalStore.java +++ b/src/com/fsck/k9/mail/store/LocalStore.java @@ -15,6 +15,7 @@ import java.util.Set; import java.util.UUID; import java.util.regex.Matcher; +import com.fsck.k9.helper.HtmlToTextTagHandler; import org.apache.commons.io.IOUtils; import android.app.Application; @@ -2369,7 +2370,7 @@ public class LocalStore extends Store implements Serializable // If we couldn't generate a reasonable preview from the text part, try doing it with the HTML part. if (preview == null || preview.length() == 0) { - preview = calculateContentPreview(Html.fromHtml(html).toString().replace(PREVIEW_OBJECT_CHARACTER, PREVIEW_OBJECT_REPLACEMENT)); + preview = calculateContentPreview(Html.fromHtml(html, null, new HtmlToTextTagHandler()).toString().replace(PREVIEW_OBJECT_CHARACTER, PREVIEW_OBJECT_REPLACEMENT)); } try @@ -2497,7 +2498,7 @@ public class LocalStore extends Store implements Serializable // If we couldn't generate a reasonable preview from the text part, try doing it with the HTML part. if (preview == null || preview.length() == 0) { - preview = calculateContentPreview(Html.fromHtml(html).toString().replace(PREVIEW_OBJECT_CHARACTER, PREVIEW_OBJECT_REPLACEMENT)); + preview = calculateContentPreview(Html.fromHtml(html, null, new HtmlToTextTagHandler()).toString().replace(PREVIEW_OBJECT_CHARACTER, PREVIEW_OBJECT_REPLACEMENT)); } try {