From 03cce0500a98a013a5ebe1e02c50ff0efd54b350 Mon Sep 17 00:00:00 2001 From: M66B Date: Sat, 3 Oct 2020 14:26:21 +0200 Subject: [PATCH] Revert "Updated HTML patch (SDK 30)" This reverts commit f3b2231180761a6942921c1243edb1f83e5594c3. --- patches/Html.patch | 1197 +++++--------------------------------------- 1 file changed, 133 insertions(+), 1064 deletions(-) diff --git a/patches/Html.patch b/patches/Html.patch index 61ee5d359e..aa21d7bc91 100644 --- a/patches/Html.patch +++ b/patches/Html.patch @@ -1,1203 +1,272 @@ ---- /home/marcel/Android/Sdk/sources/android-30/android/text/Html.java 2020-09-10 08:18:47.009487012 +0200 -+++ ./app/src/main/java/eu/faircode/email/HtmlEx.java 2020-10-03 11:19:59.338719036 +0200 -@@ -1,3 +1,5 @@ -+package eu.faircode.email; -+ - /* - * Copyright (C) 2007 The Android Open Source Project - * -@@ -14,15 +16,12 @@ - * limitations under the License. - */ - --package android.text; -- --import android.app.ActivityThread; --import android.app.Application; --import android.compat.annotation.UnsupportedAppUsage; --import android.content.res.Resources; --import android.graphics.Color; -+import android.content.Context; - import android.graphics.Typeface; --import android.graphics.drawable.Drawable; -+import android.text.Layout; -+import android.text.Spanned; -+import android.text.TextDirectionHeuristics; -+import android.text.TextUtils; - import android.text.style.AbsoluteSizeSpan; - import android.text.style.AlignmentSpan; - import android.text.style.BackgroundColorSpan; -@@ -41,213 +40,22 @@ import android.text.style.TypefaceSpan; - import android.text.style.URLSpan; - import android.text.style.UnderlineSpan; - --import org.ccil.cowan.tagsoup.HTMLSchema; --import org.ccil.cowan.tagsoup.Parser; --import org.xml.sax.Attributes; --import org.xml.sax.ContentHandler; --import org.xml.sax.InputSource; --import org.xml.sax.Locator; --import org.xml.sax.SAXException; --import org.xml.sax.XMLReader; -- --import java.io.IOException; --import java.io.StringReader; --import java.util.HashMap; --import java.util.Locale; --import java.util.Map; --import java.util.regex.Matcher; --import java.util.regex.Pattern; -- --/** -- * This class processes HTML strings into displayable styled text. -- * Not all HTML tags are supported. -- */ --public class Html { -- /** -- * Retrieves images for HTML <img> tags. -- */ -- public static interface ImageGetter { -- /** -- * This method is called when the HTML parser encounters an -- * <img> tag. The source argument is the -- * string from the "src" attribute; the return value should be -- * a Drawable representation of the image or null -- * for a generic replacement image. Make sure you call -- * setBounds() on your Drawable if it doesn't already have -- * its bounds set. -- */ -- public Drawable getDrawable(String source); -- } -- -- /** -- * Is notified when HTML tags are encountered that the parser does -- * not know how to interpret. -- */ -- public static interface TagHandler { -- /** -- * This method will be called whenn the HTML parser encounters -- * a tag that it does not know how to interpret. -- */ -- public void handleTag(boolean opening, String tag, -- Editable output, XMLReader xmlReader); -- } -- -- /** -- * Option for {@link #toHtml(Spanned, int)}: Wrap consecutive lines of text delimited by '\n' -- * inside <p> elements. {@link BulletSpan}s are ignored. -- */ -- public static final int TO_HTML_PARAGRAPH_LINES_CONSECUTIVE = 0x00000000; -- -- /** -- * Option for {@link #toHtml(Spanned, int)}: Wrap each line of text delimited by '\n' inside a -- * <p> or a <li> element. This allows {@link ParagraphStyle}s attached to be -- * encoded as CSS styles within the corresponding <p> or <li> element. -- */ -- public static final int TO_HTML_PARAGRAPH_LINES_INDIVIDUAL = 0x00000001; -- -- /** -- * Flag indicating that texts inside <p> elements will be separated from other texts with -- * one newline character by default. -- */ -- public static final int FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH = 0x00000001; -- -- /** -- * Flag indicating that texts inside <h1>~<h6> elements will be separated from -- * other texts with one newline character by default. -- */ -- public static final int FROM_HTML_SEPARATOR_LINE_BREAK_HEADING = 0x00000002; -- -- /** -- * Flag indicating that texts inside <li> elements will be separated from other texts -- * with one newline character by default. -- */ -- public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM = 0x00000004; -- -- /** -- * Flag indicating that texts inside <ul> elements will be separated from other texts -- * with one newline character by default. -- */ -- public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST = 0x00000008; -- -- /** -- * Flag indicating that texts inside <div> elements will be separated from other texts -- * with one newline character by default. -- */ -- public static final int FROM_HTML_SEPARATOR_LINE_BREAK_DIV = 0x00000010; -- -- /** -- * Flag indicating that texts inside <blockquote> elements will be separated from other -- * texts with one newline character by default. -- */ -- public static final int FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE = 0x00000020; -- -- /** -- * Flag indicating that CSS color values should be used instead of those defined in -- * {@link Color}. -- */ -- public static final int FROM_HTML_OPTION_USE_CSS_COLORS = 0x00000100; -- -- /** -- * Flags for {@link #fromHtml(String, int, ImageGetter, TagHandler)}: Separate block-level -- * elements with blank lines (two newline characters) in between. This is the legacy behavior -- * prior to N. -- */ -- public static final int FROM_HTML_MODE_LEGACY = 0x00000000; -+import static android.text.Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE; - -- /** -- * Flags for {@link #fromHtml(String, int, ImageGetter, TagHandler)}: Separate block-level -- * elements with line breaks (single newline character) in between. This inverts the -- * {@link Spanned} to HTML string conversion done with the option -- * {@link #TO_HTML_PARAGRAPH_LINES_INDIVIDUAL}. -- */ -- public static final int FROM_HTML_MODE_COMPACT = -- FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH -- | FROM_HTML_SEPARATOR_LINE_BREAK_HEADING -- | FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM -- | FROM_HTML_SEPARATOR_LINE_BREAK_LIST -- | FROM_HTML_SEPARATOR_LINE_BREAK_DIV -- | FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE; -+public class HtmlEx { -+ private Context context; - -- /** -- * The bit which indicates if lines delimited by '\n' will be grouped into <p> elements. -- */ - private static final int TO_HTML_PARAGRAPH_FLAG = 0x00000001; - -- private Html() { } -- -- /** -- * Returns displayable styled text from the provided HTML string with the legacy flags -- * {@link #FROM_HTML_MODE_LEGACY}. -- * -- * @deprecated use {@link #fromHtml(String, int)} instead. -- */ -- @Deprecated -- public static Spanned fromHtml(String source) { -- return fromHtml(source, FROM_HTML_MODE_LEGACY, null, null); -- } -- -- /** -- * Returns displayable styled text from the provided HTML string. Any <img> tags in the -- * HTML will display as a generic replacement image which your program can then go through and -- * replace with real images. -- * -- *

This uses TagSoup to handle real HTML, including all of the brokenness found in the wild. -- */ -- public static Spanned fromHtml(String source, int flags) { -- return fromHtml(source, flags, null, null); -- } -- -- /** -- * Lazy initialization holder for HTML parser. This class will -- * a) be preloaded by the zygote, or b) not loaded until absolutely -- * necessary. -- */ -- private static class HtmlParser { -- private static final HTMLSchema schema = new HTMLSchema(); -- } -- -- /** -- * Returns displayable styled text from the provided HTML string with the legacy flags -- * {@link #FROM_HTML_MODE_LEGACY}. -- * -- * @deprecated use {@link #fromHtml(String, int, ImageGetter, TagHandler)} instead. -- */ -- @Deprecated -- public static Spanned fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler) { -- return fromHtml(source, FROM_HTML_MODE_LEGACY, imageGetter, tagHandler); -- } -- -- /** -- * Returns displayable styled text from the provided HTML string. Any <img> tags in the -- * HTML will use the specified ImageGetter to request a representation of the image (use null -- * if you don't want this) and the specified TagHandler to handle unknown tags (specify null if -- * you don't want this). -- * -- *

This uses TagSoup to handle real HTML, including all of the brokenness found in the wild. -- */ -- public static Spanned fromHtml(String source, int flags, ImageGetter imageGetter, -- TagHandler tagHandler) { -- Parser parser = new Parser(); -- try { -- parser.setProperty(Parser.schemaProperty, HtmlParser.schema); -- } catch (org.xml.sax.SAXNotRecognizedException e) { -- // Should not happen. -- throw new RuntimeException(e); -- } catch (org.xml.sax.SAXNotSupportedException e) { -- // Should not happen. -- throw new RuntimeException(e); -- } -- -- HtmlToSpannedConverter converter = -- new HtmlToSpannedConverter(source, imageGetter, tagHandler, parser, flags); -- return converter.convert(); -+ public HtmlEx(Context context){ -+ this.context = context; - } - - /** +diff --git a/app/src/main/java/eu/faircode/email/HtmlEx.java b/app/src/main/java/eu/faircode/email/HtmlEx.java +index af06a672b..bbfcdc5fc 100644 +--- a/app/src/main/java/eu/faircode/email/HtmlEx.java ++++ b/app/src/main/java/eu/faircode/email/HtmlEx.java +@@ -55,7 +55,7 @@ public class HtmlEx { * @deprecated use {@link #toHtml(Spanned, int)} instead. */ @Deprecated -- public static String toHtml(Spanned text) { -+ public /* static */ String toHtml(Spanned text) { +- public /* static */ String toHtml(Spanned text) { ++ public static String toHtml(Spanned text) { return toHtml(text, TO_HTML_PARAGRAPH_LINES_CONSECUTIVE); } -@@ -261,7 +69,7 @@ public class Html { +@@ -69,7 +69,7 @@ public class HtmlEx { * {@link #TO_HTML_PARAGRAPH_LINES_INDIVIDUAL} * @return string containing input converted to HTML */ -- public static String toHtml(Spanned text, int option) { -+ public /* static */ String toHtml(Spanned text, int option) { +- public /* static */ String toHtml(Spanned text, int option) { ++ public static String toHtml(Spanned text, int option) { StringBuilder out = new StringBuilder(); withinHtml(out, text, option); return out.toString(); -@@ -270,13 +78,13 @@ public class Html { +@@ -78,13 +78,13 @@ public class HtmlEx { /** * Returns an HTML escaped representation of the given plain text. */ -- public static String escapeHtml(CharSequence text) { -+ public /* static */ String escapeHtml(CharSequence text) { +- public /* static */ String escapeHtml(CharSequence text) { ++ public static String escapeHtml(CharSequence text) { StringBuilder out = new StringBuilder(); withinStyle(out, text, 0, text.length()); return out.toString(); } -- private static void withinHtml(StringBuilder out, Spanned text, int option) { -+ private /* static */ void withinHtml(StringBuilder out, Spanned text, int option) { +- private /* static */ void withinHtml(StringBuilder out, Spanned text, int option) { ++ private static void withinHtml(StringBuilder out, Spanned text, int option) { if ((option & TO_HTML_PARAGRAPH_FLAG) == TO_HTML_PARAGRAPH_LINES_CONSECUTIVE) { encodeTextAlignmentByDiv(out, text, option); return; -@@ -285,7 +93,7 @@ public class Html { +@@ -93,7 +93,7 @@ public class HtmlEx { withinDiv(out, text, 0, text.length(), option); } -- private static void encodeTextAlignmentByDiv(StringBuilder out, Spanned text, int option) { -+ private /* static */ void encodeTextAlignmentByDiv(StringBuilder out, Spanned text, int option) { +- private /* static */ void encodeTextAlignmentByDiv(StringBuilder out, Spanned text, int option) { ++ private static void encodeTextAlignmentByDiv(StringBuilder out, Spanned text, int option) { int len = text.length(); int next; -@@ -298,7 +106,7 @@ public class Html { - for(int j = 0; j < style.length; j++) { - if (style[j] instanceof AlignmentSpan) { - Layout.Alignment align = -- ((AlignmentSpan) style[j]).getAlignment(); -+ ((AlignmentSpan) style[j]).getAlignment(); - needDiv = true; - if (align == Layout.Alignment.ALIGN_CENTER) { - elements = "align=\"center\" " + elements; -@@ -321,8 +129,8 @@ public class Html { +@@ -129,7 +129,7 @@ public class HtmlEx { } } -- private static void withinDiv(StringBuilder out, Spanned text, int start, int end, -- int option) { -+ private /* static */ void withinDiv(StringBuilder out, Spanned text, int start, int end, -+ int option) { +- private /* static */ void withinDiv(StringBuilder out, Spanned text, int start, int end, ++ private static void withinDiv(StringBuilder out, Spanned text, int start, int end, + int option) { int next; for (int i = start; i < end; i = next) { - next = text.nextSpanTransition(i, end, QuoteSpan.class); -@@ -340,7 +148,7 @@ public class Html { +@@ -148,7 +148,7 @@ public class HtmlEx { } } -- private static String getTextDirection(Spanned text, int start, int end) { -+ private /* static */ String getTextDirection(Spanned text, int start, int end) { +- private /* static */ String getTextDirection(Spanned text, int start, int end) { ++ private static String getTextDirection(Spanned text, int start, int end) { if (TextDirectionHeuristics.FIRSTSTRONG_LTR.isRtl(text, start, end - start)) { return " dir=\"rtl\""; } else { -@@ -348,8 +156,8 @@ public class Html { +@@ -156,7 +156,7 @@ public class HtmlEx { } } -- private static String getTextStyles(Spanned text, int start, int end, -- boolean forceNoVerticalMargin, boolean includeTextAlign) { -+ private /* static */ String getTextStyles(Spanned text, int start, int end, -+ boolean forceNoVerticalMargin, boolean includeTextAlign) { +- private /* static */ String getTextStyles(Spanned text, int start, int end, ++ private static String getTextStyles(Spanned text, int start, int end, + boolean forceNoVerticalMargin, boolean includeTextAlign) { String margin = null; String textAlign = null; - -@@ -362,7 +170,7 @@ public class Html { - // Only use the last AlignmentSpan with flag SPAN_PARAGRAPH - for (int i = alignmentSpans.length - 1; i >= 0; i--) { - AlignmentSpan s = alignmentSpans[i]; -- if ((text.getSpanFlags(s) & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH) { -+ if ((text.getSpanFlags(s) & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH || true) { - final Layout.Alignment alignment = s.getAlignment(); - if (alignment == Layout.Alignment.ALIGN_NORMAL) { - textAlign = "text-align:start;"; -@@ -392,8 +200,8 @@ public class Html { +@@ -200,7 +200,7 @@ public class HtmlEx { return style.append("\"").toString(); } -- private static void withinBlockquote(StringBuilder out, Spanned text, int start, int end, -- int option) { -+ private /* static */ void withinBlockquote(StringBuilder out, Spanned text, int start, int end, -+ int option) { +- private /* static */ void withinBlockquote(StringBuilder out, Spanned text, int start, int end, ++ private static void withinBlockquote(StringBuilder out, Spanned text, int start, int end, + int option) { if ((option & TO_HTML_PARAGRAPH_FLAG) == TO_HTML_PARAGRAPH_LINES_CONSECUTIVE) { withinBlockquoteConsecutive(out, text, start, end); - } else { -@@ -401,9 +209,9 @@ public class Html { +@@ -209,9 +209,9 @@ public class HtmlEx { } } -- private static void withinBlockquoteIndividual(StringBuilder out, Spanned text, int start, -- int end) { -- boolean isInList = false; -+ private /* static */ void withinBlockquoteIndividual(StringBuilder out, Spanned text, int start, -+ int end) { -+ Boolean isInBulletList = null; +- private /* static */ void withinBlockquoteIndividual(StringBuilder out, Spanned text, int start, ++ private static void withinBlockquoteIndividual(StringBuilder out, Spanned text, int start, + int end) { +- Boolean isInBulletList = null; ++ boolean isInList = false; int next; for (int i = start; i <= end; i = next) { next = TextUtils.indexOf(text, '\n', i, end); -@@ -412,42 +220,48 @@ public class Html { +@@ -220,48 +220,42 @@ public class HtmlEx { } if (next == i) { -- if (isInList) { -+ if (isInBulletList != null) { +- if (isInBulletList != null) { ++ if (isInList) { // Current paragraph is no longer a list item; close the previously opened list -- isInList = false; -- out.append("\n"); -+ out.append(isInBulletList ? "\n" : "\n"); -+ isInBulletList = null; +- out.append(isInBulletList ? "\n" : "\n"); +- isInBulletList = null; ++ isInList = false; ++ out.append("\n"); } -- out.append("
\n"); -+ if (i != text.length()) -+ out.append("
\n"); +- if (i != text.length()) +- out.append("
\n"); ++ out.append("
\n"); } else { -- boolean isListItem = false; -+ Boolean isBulletListItem = null; +- Boolean isBulletListItem = null; ++ boolean isListItem = false; ParagraphStyle[] paragraphStyles = text.getSpans(i, next, ParagraphStyle.class); for (ParagraphStyle paragraphStyle : paragraphStyles) { final int spanFlags = text.getSpanFlags(paragraphStyle); if ((spanFlags & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH && paragraphStyle instanceof BulletSpan) { -- isListItem = true; -+ isBulletListItem = !(paragraphStyle instanceof eu.faircode.email.NumberSpan); +- isBulletListItem = !(paragraphStyle instanceof eu.faircode.email.NumberSpan); ++ isListItem = true; break; } } -- if (isListItem && !isInList) { -+ if (isBulletListItem != null && isInBulletList != null && isBulletListItem != isInBulletList) { -+ out.append(isInBulletList ? "\n" : "\n"); -+ isInBulletList = null; -+ } -+ -+ if (isBulletListItem != null && isInBulletList == null) { +- if (isBulletListItem != null && isInBulletList != null && isBulletListItem != isInBulletList) { +- out.append(isInBulletList ? "\n" : "\n"); +- isInBulletList = null; +- } +- +- if (isBulletListItem != null && isInBulletList == null) { ++ if (isListItem && !isInList) { // Current paragraph is the first item in a list -- isInList = true; -- out.append("\n"); } -- if (isInList && !isListItem) { -+ if (isInBulletList != null && isBulletListItem == null) { +- if (isInBulletList != null && isBulletListItem == null) { ++ if (isInList && !isListItem) { // Current paragraph is no longer a list item; close the previously opened list -- isInList = false; -- out.append("\n"); -+ out.append(isInBulletList ? "\n" : "\n"); -+ isInBulletList = null; +- out.append(isInBulletList ? "\n" : "\n"); +- isInBulletList = null; ++ isInList = false; ++ out.append("\n"); } -- String tagType = isListItem ? "li" : "p"; -+ String tagType = isBulletListItem != null ? "li" : "span"; +- String tagType = isBulletListItem != null ? "li" : "span"; ++ String tagType = isListItem ? "li" : "p"; out.append("<").append(tagType) .append(getTextDirection(text, i, next)) -- .append(getTextStyles(text, i, next, !isListItem, true)) -+ .append(getTextStyles(text, i, next, isBulletListItem == null, true)) +- .append(getTextStyles(text, i, next, isBulletListItem == null, true)) ++ .append(getTextStyles(text, i, next, !isListItem, true)) .append(">"); withinParagraph(out, text, i, next); -@@ -455,10 +269,12 @@ public class Html { +@@ -269,12 +263,10 @@ public class HtmlEx { out.append("\n"); -+ if (isBulletListItem == null) -+ out.append("
\n"); - -- if (next == end && isInList) { -- isInList = false; -- out.append("\n"); -+ if (next == end && isInBulletList != null) { -+ out.append(isInBulletList ? "\n" : "\n"); -+ isInBulletList = null; +- if (isBulletListItem == null) +- out.append("
\n"); + +- if (next == end && isInBulletList != null) { +- out.append(isInBulletList ? "\n" : "\n"); +- isInBulletList = null; ++ if (next == end && isInList) { ++ isInList = false; ++ out.append("\n"); } } -@@ -466,9 +282,9 @@ public class Html { +@@ -282,9 +274,9 @@ public class HtmlEx { } } -- private static void withinBlockquoteConsecutive(StringBuilder out, Spanned text, int start, -- int end) { -- out.append(""); -+ private /* static */ void withinBlockquoteConsecutive(StringBuilder out, Spanned text, int start, -+ int end) { -+ out.append(""); +- private /* static */ void withinBlockquoteConsecutive(StringBuilder out, Spanned text, int start, ++ private static void withinBlockquoteConsecutive(StringBuilder out, Spanned text, int start, + int end) { +- out.append(""); ++ out.append(""); int next; for (int i = start; i < end; i = next) { -@@ -486,24 +302,24 @@ public class Html { +@@ -302,24 +294,24 @@ public class HtmlEx { withinParagraph(out, text, i, next - nl); -- if (nl == 1) { -+ if (nl == 0) { +- if (nl == 0) { ++ if (nl == 1) { out.append("
\n"); } else { -- for (int j = 2; j < nl; j++) { -+ for (int j = 0; j < nl; j++) { +- for (int j = 0; j < nl; j++) { ++ for (int j = 2; j < nl; j++) { out.append("
"); } if (next != end) { /* Paragraph should be closed and reopened */ -- out.append("

\n"); -- out.append(""); -+ out.append("\n"); -+ out.append(""); +- out.append("\n"); +- out.append(""); ++ out.append("

\n"); ++ out.append(""); } } } -- out.append("

\n"); -+ out.append("\n"); +- out.append("\n"); ++ out.append("

\n"); } -- private static void withinParagraph(StringBuilder out, Spanned text, int start, int end) { -+ private /* static */ void withinParagraph(StringBuilder out, Spanned text, int start, int end) { +- private /* static */ void withinParagraph(StringBuilder out, Spanned text, int start, int end) { ++ private static void withinParagraph(StringBuilder out, Spanned text, int start, int end) { int next; for (int i = start; i < end; i = next) { next = text.nextSpanTransition(i, end, CharacterStyle.class); -@@ -523,9 +339,11 @@ public class Html { +@@ -339,11 +331,9 @@ public class HtmlEx { if (style[j] instanceof TypefaceSpan) { String s = ((TypefaceSpan) style[j]).getFamily(); -- if ("monospace".equals(s)) { -- out.append(""); -- } -+ //if ("monospace".equals(s)) { -+ // out.append(""); -+ //} -+ -+ out.append(""); +- //if ("monospace".equals(s)) { +- // out.append(""); +- //} +- +- out.append(""); ++ if ("monospace".equals(s)) { ++ out.append(""); ++ } } if (style[j] instanceof SuperscriptSpan) { out.append(""); -@@ -556,8 +374,8 @@ public class Html { +@@ -374,8 +364,8 @@ public class HtmlEx { AbsoluteSizeSpan s = ((AbsoluteSizeSpan) style[j]); float sizeDip = s.getSize(); if (!s.getDip()) { -- Application application = ActivityThread.currentApplication(); -- sizeDip /= application.getResources().getDisplayMetrics().density; -+ //Application application = ActivityThread.currentApplication(); -+ sizeDip /= context.getResources().getDisplayMetrics().density; +- //Application application = ActivityThread.currentApplication(); +- sizeDip /= context.getResources().getDisplayMetrics().density; ++ Application application = ActivityThread.currentApplication(); ++ sizeDip /= application.getResources().getDisplayMetrics().density; } // px in CSS is the equivalance of dip in Android -@@ -609,11 +427,13 @@ public class Html { +@@ -427,13 +417,11 @@ public class HtmlEx { out.append(""); } if (style[j] instanceof TypefaceSpan) { -- String s = ((TypefaceSpan) style[j]).getFamily(); -+ //String s = ((TypefaceSpan) style[j]).getFamily(); +- //String s = ((TypefaceSpan) style[j]).getFamily(); +- +- //if (s.equals("monospace")) { +- // out.append(""); +- //} ++ String s = ((TypefaceSpan) style[j]).getFamily(); -- if (s.equals("monospace")) { -- out.append(""); -- } -+ //if (s.equals("monospace")) { -+ // out.append(""); -+ //} -+ -+ out.append(""); +- out.append(""); ++ if (s.equals("monospace")) { ++ out.append(""); ++ } } if (style[j] instanceof StyleSpan) { int s = ((StyleSpan) style[j]).getStyle(); -@@ -629,8 +449,8 @@ public class Html { +@@ -449,8 +437,8 @@ public class HtmlEx { } } -- @UnsupportedAppUsage -- private static void withinStyle(StringBuilder out, CharSequence text, -+ //@UnsupportedAppUsage -+ private /* static */ void withinStyle(StringBuilder out, CharSequence text, +- //@UnsupportedAppUsage +- private /* static */ void withinStyle(StringBuilder out, CharSequence text, ++ @UnsupportedAppUsage ++ private static void withinStyle(StringBuilder out, CharSequence text, int start, int end) { for (int i = start; i < end; i++) { char c = text.charAt(i); -@@ -665,668 +485,3 @@ public class Html { - } - } - } -- --class HtmlToSpannedConverter implements ContentHandler { -- -- private static final float[] HEADING_SIZES = { -- 1.5f, 1.4f, 1.3f, 1.2f, 1.1f, 1f, -- }; -- -- private String mSource; -- private XMLReader mReader; -- private SpannableStringBuilder mSpannableStringBuilder; -- private Html.ImageGetter mImageGetter; -- private Html.TagHandler mTagHandler; -- private int mFlags; -- -- private static Pattern sTextAlignPattern; -- private static Pattern sForegroundColorPattern; -- private static Pattern sBackgroundColorPattern; -- private static Pattern sTextDecorationPattern; -- -- /** -- * Name-value mapping of HTML/CSS colors which have different values in {@link Color}. -- */ -- private static final Map sColorMap; -- -- static { -- sColorMap = new HashMap<>(); -- sColorMap.put("darkgray", 0xFFA9A9A9); -- sColorMap.put("gray", 0xFF808080); -- sColorMap.put("lightgray", 0xFFD3D3D3); -- sColorMap.put("darkgrey", 0xFFA9A9A9); -- sColorMap.put("grey", 0xFF808080); -- sColorMap.put("lightgrey", 0xFFD3D3D3); -- sColorMap.put("green", 0xFF008000); -- } -- -- private static Pattern getTextAlignPattern() { -- if (sTextAlignPattern == null) { -- sTextAlignPattern = Pattern.compile("(?:\\s+|\\A)text-align\\s*:\\s*(\\S*)\\b"); -- } -- return sTextAlignPattern; -- } -- -- private static Pattern getForegroundColorPattern() { -- if (sForegroundColorPattern == null) { -- sForegroundColorPattern = Pattern.compile( -- "(?:\\s+|\\A)color\\s*:\\s*(\\S*)\\b"); -- } -- return sForegroundColorPattern; -- } -- -- private static Pattern getBackgroundColorPattern() { -- if (sBackgroundColorPattern == null) { -- sBackgroundColorPattern = Pattern.compile( -- "(?:\\s+|\\A)background(?:-color)?\\s*:\\s*(\\S*)\\b"); -- } -- return sBackgroundColorPattern; -- } -- -- private static Pattern getTextDecorationPattern() { -- if (sTextDecorationPattern == null) { -- sTextDecorationPattern = Pattern.compile( -- "(?:\\s+|\\A)text-decoration\\s*:\\s*(\\S*)\\b"); -- } -- return sTextDecorationPattern; -- } -- -- public HtmlToSpannedConverter( String source, Html.ImageGetter imageGetter, -- Html.TagHandler tagHandler, Parser parser, int flags) { -- mSource = source; -- mSpannableStringBuilder = new SpannableStringBuilder(); -- mImageGetter = imageGetter; -- mTagHandler = tagHandler; -- mReader = parser; -- mFlags = flags; -- } -- -- public Spanned convert() { -- -- mReader.setContentHandler(this); -- try { -- mReader.parse(new InputSource(new StringReader(mSource))); -- } catch (IOException e) { -- // We are reading from a string. There should not be IO problems. -- throw new RuntimeException(e); -- } catch (SAXException e) { -- // TagSoup doesn't throw parse exceptions. -- throw new RuntimeException(e); -- } -- -- // Fix flags and range for paragraph-type markup. -- Object[] obj = mSpannableStringBuilder.getSpans(0, mSpannableStringBuilder.length(), ParagraphStyle.class); -- for (int i = 0; i < obj.length; i++) { -- int start = mSpannableStringBuilder.getSpanStart(obj[i]); -- int end = mSpannableStringBuilder.getSpanEnd(obj[i]); -- -- // If the last line of the range is blank, back off by one. -- if (end - 2 >= 0) { -- if (mSpannableStringBuilder.charAt(end - 1) == '\n' && -- mSpannableStringBuilder.charAt(end - 2) == '\n') { -- end--; -- } -- } -- -- if (end == start) { -- mSpannableStringBuilder.removeSpan(obj[i]); -- } else { -- mSpannableStringBuilder.setSpan(obj[i], start, end, Spannable.SPAN_PARAGRAPH); -- } -- } -- -- return mSpannableStringBuilder; -- } -- -- private void handleStartTag(String tag, Attributes attributes) { -- if (tag.equalsIgnoreCase("br")) { -- // We don't need to handle this. TagSoup will ensure that there's a
for each
-- // so we can safely emit the linebreaks when we handle the close tag. -- } else if (tag.equalsIgnoreCase("p")) { -- startBlockElement(mSpannableStringBuilder, attributes, getMarginParagraph()); -- startCssStyle(mSpannableStringBuilder, attributes); -- } else if (tag.equalsIgnoreCase("ul")) { -- startBlockElement(mSpannableStringBuilder, attributes, getMarginList()); -- } else if (tag.equalsIgnoreCase("li")) { -- startLi(mSpannableStringBuilder, attributes); -- } else if (tag.equalsIgnoreCase("div")) { -- startBlockElement(mSpannableStringBuilder, attributes, getMarginDiv()); -- } else if (tag.equalsIgnoreCase("span")) { -- startCssStyle(mSpannableStringBuilder, attributes); -- } else if (tag.equalsIgnoreCase("strong")) { -- start(mSpannableStringBuilder, new Bold()); -- } else if (tag.equalsIgnoreCase("b")) { -- start(mSpannableStringBuilder, new Bold()); -- } else if (tag.equalsIgnoreCase("em")) { -- start(mSpannableStringBuilder, new Italic()); -- } else if (tag.equalsIgnoreCase("cite")) { -- start(mSpannableStringBuilder, new Italic()); -- } else if (tag.equalsIgnoreCase("dfn")) { -- start(mSpannableStringBuilder, new Italic()); -- } else if (tag.equalsIgnoreCase("i")) { -- start(mSpannableStringBuilder, new Italic()); -- } else if (tag.equalsIgnoreCase("big")) { -- start(mSpannableStringBuilder, new Big()); -- } else if (tag.equalsIgnoreCase("small")) { -- start(mSpannableStringBuilder, new Small()); -- } else if (tag.equalsIgnoreCase("font")) { -- startFont(mSpannableStringBuilder, attributes); -- } else if (tag.equalsIgnoreCase("blockquote")) { -- startBlockquote(mSpannableStringBuilder, attributes); -- } else if (tag.equalsIgnoreCase("tt")) { -- start(mSpannableStringBuilder, new Monospace()); -- } else if (tag.equalsIgnoreCase("a")) { -- startA(mSpannableStringBuilder, attributes); -- } else if (tag.equalsIgnoreCase("u")) { -- start(mSpannableStringBuilder, new Underline()); -- } else if (tag.equalsIgnoreCase("del")) { -- start(mSpannableStringBuilder, new Strikethrough()); -- } else if (tag.equalsIgnoreCase("s")) { -- start(mSpannableStringBuilder, new Strikethrough()); -- } else if (tag.equalsIgnoreCase("strike")) { -- start(mSpannableStringBuilder, new Strikethrough()); -- } else if (tag.equalsIgnoreCase("sup")) { -- start(mSpannableStringBuilder, new Super()); -- } else if (tag.equalsIgnoreCase("sub")) { -- start(mSpannableStringBuilder, new Sub()); -- } else if (tag.length() == 2 && -- Character.toLowerCase(tag.charAt(0)) == 'h' && -- tag.charAt(1) >= '1' && tag.charAt(1) <= '6') { -- startHeading(mSpannableStringBuilder, attributes, tag.charAt(1) - '1'); -- } else if (tag.equalsIgnoreCase("img")) { -- startImg(mSpannableStringBuilder, attributes, mImageGetter); -- } else if (mTagHandler != null) { -- mTagHandler.handleTag(true, tag, mSpannableStringBuilder, mReader); -- } -- } -- -- private void handleEndTag(String tag) { -- if (tag.equalsIgnoreCase("br")) { -- handleBr(mSpannableStringBuilder); -- } else if (tag.equalsIgnoreCase("p")) { -- endCssStyle(mSpannableStringBuilder); -- endBlockElement(mSpannableStringBuilder); -- } else if (tag.equalsIgnoreCase("ul")) { -- endBlockElement(mSpannableStringBuilder); -- } else if (tag.equalsIgnoreCase("li")) { -- endLi(mSpannableStringBuilder); -- } else if (tag.equalsIgnoreCase("div")) { -- endBlockElement(mSpannableStringBuilder); -- } else if (tag.equalsIgnoreCase("span")) { -- endCssStyle(mSpannableStringBuilder); -- } else if (tag.equalsIgnoreCase("strong")) { -- end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD)); -- } else if (tag.equalsIgnoreCase("b")) { -- end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD)); -- } else if (tag.equalsIgnoreCase("em")) { -- end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); -- } else if (tag.equalsIgnoreCase("cite")) { -- end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); -- } else if (tag.equalsIgnoreCase("dfn")) { -- end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); -- } else if (tag.equalsIgnoreCase("i")) { -- end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); -- } else if (tag.equalsIgnoreCase("big")) { -- end(mSpannableStringBuilder, Big.class, new RelativeSizeSpan(1.25f)); -- } else if (tag.equalsIgnoreCase("small")) { -- end(mSpannableStringBuilder, Small.class, new RelativeSizeSpan(0.8f)); -- } else if (tag.equalsIgnoreCase("font")) { -- endFont(mSpannableStringBuilder); -- } else if (tag.equalsIgnoreCase("blockquote")) { -- endBlockquote(mSpannableStringBuilder); -- } else if (tag.equalsIgnoreCase("tt")) { -- end(mSpannableStringBuilder, Monospace.class, new TypefaceSpan("monospace")); -- } else if (tag.equalsIgnoreCase("a")) { -- endA(mSpannableStringBuilder); -- } else if (tag.equalsIgnoreCase("u")) { -- end(mSpannableStringBuilder, Underline.class, new UnderlineSpan()); -- } else if (tag.equalsIgnoreCase("del")) { -- end(mSpannableStringBuilder, Strikethrough.class, new StrikethroughSpan()); -- } else if (tag.equalsIgnoreCase("s")) { -- end(mSpannableStringBuilder, Strikethrough.class, new StrikethroughSpan()); -- } else if (tag.equalsIgnoreCase("strike")) { -- end(mSpannableStringBuilder, Strikethrough.class, new StrikethroughSpan()); -- } else if (tag.equalsIgnoreCase("sup")) { -- end(mSpannableStringBuilder, Super.class, new SuperscriptSpan()); -- } else if (tag.equalsIgnoreCase("sub")) { -- end(mSpannableStringBuilder, Sub.class, new SubscriptSpan()); -- } else if (tag.length() == 2 && -- Character.toLowerCase(tag.charAt(0)) == 'h' && -- tag.charAt(1) >= '1' && tag.charAt(1) <= '6') { -- endHeading(mSpannableStringBuilder); -- } else if (mTagHandler != null) { -- mTagHandler.handleTag(false, tag, mSpannableStringBuilder, mReader); -- } -- } -- -- private int getMarginParagraph() { -- return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH); -- } -- -- private int getMarginHeading() { -- return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING); -- } -- -- private int getMarginListItem() { -- return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM); -- } -- -- private int getMarginList() { -- return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST); -- } -- -- private int getMarginDiv() { -- return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_DIV); -- } -- -- private int getMarginBlockquote() { -- return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE); -- } -- -- /** -- * Returns the minimum number of newline characters needed before and after a given block-level -- * element. -- * -- * @param flag the corresponding option flag defined in {@link Html} of a block-level element -- */ -- private int getMargin(int flag) { -- if ((flag & mFlags) != 0) { -- return 1; -- } -- return 2; -- } -- -- private static void appendNewlines(Editable text, int minNewline) { -- final int len = text.length(); -- -- if (len == 0) { -- return; -- } -- -- int existingNewlines = 0; -- for (int i = len - 1; i >= 0 && text.charAt(i) == '\n'; i--) { -- existingNewlines++; -- } -- -- for (int j = existingNewlines; j < minNewline; j++) { -- text.append("\n"); -- } -- } -- -- private static void startBlockElement(Editable text, Attributes attributes, int margin) { -- final int len = text.length(); -- if (margin > 0) { -- appendNewlines(text, margin); -- start(text, new Newline(margin)); -- } -- -- String style = attributes.getValue("", "style"); -- if (style != null) { -- Matcher m = getTextAlignPattern().matcher(style); -- if (m.find()) { -- String alignment = m.group(1); -- if (alignment.equalsIgnoreCase("start")) { -- start(text, new Alignment(Layout.Alignment.ALIGN_NORMAL)); -- } else if (alignment.equalsIgnoreCase("center")) { -- start(text, new Alignment(Layout.Alignment.ALIGN_CENTER)); -- } else if (alignment.equalsIgnoreCase("end")) { -- start(text, new Alignment(Layout.Alignment.ALIGN_OPPOSITE)); -- } -- } -- } -- } -- -- private static void endBlockElement(Editable text) { -- Newline n = getLast(text, Newline.class); -- if (n != null) { -- appendNewlines(text, n.mNumNewlines); -- text.removeSpan(n); -- } -- -- Alignment a = getLast(text, Alignment.class); -- if (a != null) { -- setSpanFromMark(text, a, new AlignmentSpan.Standard(a.mAlignment)); -- } -- } -- -- private static void handleBr(Editable text) { -- text.append('\n'); -- } -- -- private void startLi(Editable text, Attributes attributes) { -- startBlockElement(text, attributes, getMarginListItem()); -- start(text, new Bullet()); -- startCssStyle(text, attributes); -- } -- -- private static void endLi(Editable text) { -- endCssStyle(text); -- endBlockElement(text); -- end(text, Bullet.class, new BulletSpan()); -- } -- -- private void startBlockquote(Editable text, Attributes attributes) { -- startBlockElement(text, attributes, getMarginBlockquote()); -- start(text, new Blockquote()); -- } -- -- private static void endBlockquote(Editable text) { -- endBlockElement(text); -- end(text, Blockquote.class, new QuoteSpan()); -- } -- -- private void startHeading(Editable text, Attributes attributes, int level) { -- startBlockElement(text, attributes, getMarginHeading()); -- start(text, new Heading(level)); -- } -- -- private static void endHeading(Editable text) { -- // RelativeSizeSpan and StyleSpan are CharacterStyles -- // Their ranges should not include the newlines at the end -- Heading h = getLast(text, Heading.class); -- if (h != null) { -- setSpanFromMark(text, h, new RelativeSizeSpan(HEADING_SIZES[h.mLevel]), -- new StyleSpan(Typeface.BOLD)); -- } -- -- endBlockElement(text); -- } -- -- private static T getLast(Spanned text, Class kind) { -- /* -- * This knows that the last returned object from getSpans() -- * will be the most recently added. -- */ -- T[] objs = text.getSpans(0, text.length(), kind); -- -- if (objs.length == 0) { -- return null; -- } else { -- return objs[objs.length - 1]; -- } -- } -- -- private static void setSpanFromMark(Spannable text, Object mark, Object... spans) { -- int where = text.getSpanStart(mark); -- text.removeSpan(mark); -- int len = text.length(); -- if (where != len) { -- for (Object span : spans) { -- text.setSpan(span, where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); -- } -- } -- } -- -- private static void start(Editable text, Object mark) { -- int len = text.length(); -- text.setSpan(mark, len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); -- } -- -- private static void end(Editable text, Class kind, Object repl) { -- int len = text.length(); -- Object obj = getLast(text, kind); -- if (obj != null) { -- setSpanFromMark(text, obj, repl); -- } -- } -- -- private void startCssStyle(Editable text, Attributes attributes) { -- String style = attributes.getValue("", "style"); -- if (style != null) { -- Matcher m = getForegroundColorPattern().matcher(style); -- if (m.find()) { -- int c = getHtmlColor(m.group(1)); -- if (c != -1) { -- start(text, new Foreground(c | 0xFF000000)); -- } -- } -- -- m = getBackgroundColorPattern().matcher(style); -- if (m.find()) { -- int c = getHtmlColor(m.group(1)); -- if (c != -1) { -- start(text, new Background(c | 0xFF000000)); -- } -- } -- -- m = getTextDecorationPattern().matcher(style); -- if (m.find()) { -- String textDecoration = m.group(1); -- if (textDecoration.equalsIgnoreCase("line-through")) { -- start(text, new Strikethrough()); -- } -- } -- } -- } -- -- private static void endCssStyle(Editable text) { -- Strikethrough s = getLast(text, Strikethrough.class); -- if (s != null) { -- setSpanFromMark(text, s, new StrikethroughSpan()); -- } -- -- Background b = getLast(text, Background.class); -- if (b != null) { -- setSpanFromMark(text, b, new BackgroundColorSpan(b.mBackgroundColor)); -- } -- -- Foreground f = getLast(text, Foreground.class); -- if (f != null) { -- setSpanFromMark(text, f, new ForegroundColorSpan(f.mForegroundColor)); -- } -- } -- -- private static void startImg(Editable text, Attributes attributes, Html.ImageGetter img) { -- String src = attributes.getValue("", "src"); -- Drawable d = null; -- -- if (img != null) { -- d = img.getDrawable(src); -- } -- -- if (d == null) { -- d = Resources.getSystem(). -- getDrawable(com.android.internal.R.drawable.unknown_image); -- d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); -- } -- -- int len = text.length(); -- text.append("\uFFFC"); -- -- text.setSpan(new ImageSpan(d, src), len, text.length(), -- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); -- } -- -- private void startFont(Editable text, Attributes attributes) { -- String color = attributes.getValue("", "color"); -- String face = attributes.getValue("", "face"); -- -- if (!TextUtils.isEmpty(color)) { -- int c = getHtmlColor(color); -- if (c != -1) { -- start(text, new Foreground(c | 0xFF000000)); -- } -- } -- -- if (!TextUtils.isEmpty(face)) { -- start(text, new Font(face)); -- } -- } -- -- private static void endFont(Editable text) { -- Font font = getLast(text, Font.class); -- if (font != null) { -- setSpanFromMark(text, font, new TypefaceSpan(font.mFace)); -- } -- -- Foreground foreground = getLast(text, Foreground.class); -- if (foreground != null) { -- setSpanFromMark(text, foreground, -- new ForegroundColorSpan(foreground.mForegroundColor)); -- } -- } -- -- private static void startA(Editable text, Attributes attributes) { -- String href = attributes.getValue("", "href"); -- start(text, new Href(href)); -- } -- -- private static void endA(Editable text) { -- Href h = getLast(text, Href.class); -- if (h != null) { -- if (h.mHref != null) { -- setSpanFromMark(text, h, new URLSpan((h.mHref))); -- } -- } -- } -- -- private int getHtmlColor(String color) { -- if ((mFlags & Html.FROM_HTML_OPTION_USE_CSS_COLORS) -- == Html.FROM_HTML_OPTION_USE_CSS_COLORS) { -- Integer i = sColorMap.get(color.toLowerCase(Locale.US)); -- if (i != null) { -- return i; -- } -- } -- return Color.getHtmlColor(color); -- } -- -- public void setDocumentLocator(Locator locator) { -- } -- -- public void startDocument() throws SAXException { -- } -- -- public void endDocument() throws SAXException { -- } -- -- public void startPrefixMapping(String prefix, String uri) throws SAXException { -- } -- -- public void endPrefixMapping(String prefix) throws SAXException { -- } -- -- public void startElement(String uri, String localName, String qName, Attributes attributes) -- throws SAXException { -- handleStartTag(localName, attributes); -- } -- -- public void endElement(String uri, String localName, String qName) throws SAXException { -- handleEndTag(localName); -- } -- -- public void characters(char ch[], int start, int length) throws SAXException { -- StringBuilder sb = new StringBuilder(); -- -- /* -- * Ignore whitespace that immediately follows other whitespace; -- * newlines count as spaces. -- */ -- -- for (int i = 0; i < length; i++) { -- char c = ch[i + start]; -- -- if (c == ' ' || c == '\n') { -- char pred; -- int len = sb.length(); -- -- if (len == 0) { -- len = mSpannableStringBuilder.length(); -- -- if (len == 0) { -- pred = '\n'; -- } else { -- pred = mSpannableStringBuilder.charAt(len - 1); -- } -- } else { -- pred = sb.charAt(len - 1); -- } -- -- if (pred != ' ' && pred != '\n') { -- sb.append(' '); -- } -- } else { -- sb.append(c); -- } -- } -- -- mSpannableStringBuilder.append(sb); -- } -- -- public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { -- } -- -- public void processingInstruction(String target, String data) throws SAXException { -- } -- -- public void skippedEntity(String name) throws SAXException { -- } -- -- private static class Bold { } -- private static class Italic { } -- private static class Underline { } -- private static class Strikethrough { } -- private static class Big { } -- private static class Small { } -- private static class Monospace { } -- private static class Blockquote { } -- private static class Super { } -- private static class Sub { } -- private static class Bullet { } -- -- private static class Font { -- public String mFace; -- -- public Font(String face) { -- mFace = face; -- } -- } -- -- private static class Href { -- public String mHref; -- -- public Href(String href) { -- mHref = href; -- } -- } -- -- private static class Foreground { -- private int mForegroundColor; -- -- public Foreground(int foregroundColor) { -- mForegroundColor = foregroundColor; -- } -- } -- -- private static class Background { -- private int mBackgroundColor; -- -- public Background(int backgroundColor) { -- mBackgroundColor = backgroundColor; -- } -- } -- -- private static class Heading { -- private int mLevel; -- -- public Heading(int level) { -- mLevel = level; -- } -- } -- -- private static class Newline { -- private int mNumNewlines; -- -- public Newline(int numNewlines) { -- mNumNewlines = numNewlines; -- } -- } -- -- private static class Alignment { -- private Layout.Alignment mAlignment; -- -- public Alignment(Layout.Alignment alignment) { -- mAlignment = alignment; -- } -- } --}