From f3b2231180761a6942921c1243edb1f83e5594c3 Mon Sep 17 00:00:00 2001
From: M66B 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;
+ }
+
+ /**
* @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);
}
-@@ -69,7 +69,7 @@ public class HtmlEx {
+@@ -261,7 +69,7 @@ public class Html {
* {@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();
-@@ -78,13 +78,13 @@ public class HtmlEx {
+@@ -270,13 +78,13 @@ public class Html {
/**
* 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;
-@@ -93,7 +93,7 @@ public class HtmlEx {
+@@ -285,7 +93,7 @@ public class Html {
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;
-@@ -129,7 +129,7 @@ public class HtmlEx {
+@@ -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 {
}
}
-- 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) {
+- 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) {
int next;
for (int i = start; i < end; i = next) {
-@@ -148,7 +148,7 @@ public class HtmlEx {
+ next = text.nextSpanTransition(i, end, QuoteSpan.class);
+@@ -340,7 +148,7 @@ public class Html {
}
}
-- 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 {
-@@ -156,7 +156,7 @@ public class HtmlEx {
+@@ -348,8 +156,8 @@ public class Html {
}
}
-- private /* static */ String getTextStyles(Spanned text, int start, int end,
-+ 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,
++ boolean forceNoVerticalMargin, boolean includeTextAlign) {
String margin = null;
String textAlign = null;
-@@ -200,7 +200,7 @@ public class HtmlEx {
+
+@@ -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 {
return style.append("\"").toString();
}
-- 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) {
+- 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) {
if ((option & TO_HTML_PARAGRAPH_FLAG) == TO_HTML_PARAGRAPH_LINES_CONSECUTIVE) {
withinBlockquoteConsecutive(out, text, start, end);
-@@ -209,9 +209,9 @@ public class HtmlEx {
+ } else {
+@@ -401,9 +209,9 @@ public class Html {
}
}
-- 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;
+- 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;
int next;
for (int i = start; i <= end; i = next) {
next = TextUtils.indexOf(text, '\n', i, end);
-@@ -220,48 +220,42 @@ public class HtmlEx {
+@@ -412,42 +220,48 @@ public class Html {
}
if (next == i) {
-- if (isInBulletList != null) {
-+ if (isInList) {
+- if (isInList) {
++ if (isInBulletList != null) {
// Current paragraph is no longer a list item; close the previously opened list
-- out.append(isInBulletList ? "\n" : "\n");
-- isInBulletList = null;
-+ isInList = false;
-+ out.append("\n");
+- isInList = false;
+- out.append("\n");
++ out.append(isInBulletList ? "\n" : "\n");
++ isInBulletList = null;
}
-- if (i != text.length())
-- 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,
++ int end) {
++ out.append("");
int next;
for (int i = start; i < end; i = next) {
-@@ -302,24 +294,24 @@ public class HtmlEx {
+@@ -486,24 +302,24 @@ public class Html {
withinParagraph(out, text, i, next - nl);
-- if (nl == 0) {
-+ if (nl == 1) {
+- if (nl == 1) {
++ if (nl == 0) {
out.append(" ");
+- out.append(" ");
++ out.append("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.
+- *
+- *
\n");
-+ out.append("
\n");
+- out.append("
\n");
++ if (i != text.length())
++ out.append("
\n");
} else {
-- Boolean isBulletListItem = null;
-+ boolean isListItem = false;
+- boolean isListItem = false;
++ Boolean isBulletListItem = null;
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) {
-- isBulletListItem = !(paragraphStyle instanceof eu.faircode.email.NumberSpan);
-+ isListItem = true;
+- isListItem = true;
++ isBulletListItem = !(paragraphStyle instanceof eu.faircode.email.NumberSpan);
break;
}
}
-- if (isBulletListItem != null && isInBulletList != null && isBulletListItem != isInBulletList) {
-- out.append(isInBulletList ? "\n" : "\n");
-- isInBulletList = null;
-- }
--
-- if (isBulletListItem != null && isInBulletList == null) {
-+ if (isListItem && !isInList) {
+- if (isListItem && !isInList) {
++ if (isBulletListItem != null && isInBulletList != null && isBulletListItem != isInBulletList) {
++ out.append(isInBulletList ? "\n" : "\n");
++ isInBulletList = null;
++ }
++
++ if (isBulletListItem != null && isInBulletList == null) {
// Current paragraph is the first item in a list
-- isInBulletList = isBulletListItem;
-- out.append(isInBulletList ? "\n");
}
-- if (isInBulletList != null && isBulletListItem == null) {
-+ if (isInList && !isListItem) {
+- if (isInList && !isListItem) {
++ if (isInBulletList != null && isBulletListItem == null) {
// Current paragraph is no longer a list item; close the previously opened list
-- out.append(isInBulletList ? "
\n" : "\n");
-- isInBulletList = null;
-+ isInList = false;
-+ out.append("\n");
+- isInList = false;
+- out.append("\n");
++ out.append(isInBulletList ? "\n" : "\n");
++ isInBulletList = null;
}
-- String tagType = isBulletListItem != null ? "li" : "span";
-+ String tagType = isListItem ? "li" : "p";
+- String tagType = isListItem ? "li" : "p";
++ String tagType = isBulletListItem != null ? "li" : "span";
out.append("<").append(tagType)
.append(getTextDirection(text, i, next))
-- .append(getTextStyles(text, i, next, isBulletListItem == null, true))
-+ .append(getTextStyles(text, i, next, !isListItem, true))
+- .append(getTextStyles(text, i, next, !isListItem, true))
++ .append(getTextStyles(text, i, next, isBulletListItem == null, true))
.append(">");
withinParagraph(out, text, i, next);
-@@ -269,12 +263,10 @@ public class HtmlEx {
+@@ -455,10 +269,12 @@ public class Html {
out.append("");
out.append(tagType);
out.append(">\n");
-- if (isBulletListItem == null)
-- out.append("
\n");
++ 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");
+- if (next == end && isInList) {
+- isInList = false;
+- out.append("\n");
++ if (next == end && isInBulletList != null) {
++ out.append(isInBulletList ? "\n" : "\n");
++ isInBulletList = null;
}
}
-@@ -282,9 +274,9 @@ public class HtmlEx {
+@@ -466,9 +282,9 @@ public class Html {
}
}
-- 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("
\n");
} else {
-- for (int j = 0; j < nl; j++) {
-+ for (int j = 2; j < nl; j++) {
+- for (int j = 2; j < nl; j++) {
++ for (int j = 0; j < nl; j++) {
out.append("
");
}
if (next != end) {
/* Paragraph should be closed and reopened */
-- out.append("\n");
-- out.append("");
-+ out.append("