Removed custom rendering as experiment

pull/178/head
M66B 5 years ago
parent 86e589a904
commit 602621f36e

@ -2540,7 +2540,7 @@ Reformatting and displaying such messages will take too long. You can try to use
<a name="faq125"></a> <a name="faq125"></a>
**(125) What are the current experimental features?** **(125) What are the current experimental features?**
* Custom rendering of reformatted messages * ~~Custom rendering of reformatted messages~~
<br /> <br />

@ -245,7 +245,6 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
private boolean authentication; private boolean authentication;
private boolean language_detection; private boolean language_detection;
private static boolean debug; private static boolean debug;
private boolean experiments;
private boolean gotoTop = false; private boolean gotoTop = false;
private boolean firstClick = false; private boolean firstClick = false;
@ -1913,17 +1912,6 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
quote.html("&#8230;"); quote.html("&#8230;");
} }
// Add debug info
if (debug && !experiments) {
document.outputSettings().prettyPrint(true).outline(true).indentAmount(1);
String[] lines = document.html().split("\\r?\\n");
for (int i = 0; i < lines.length; i++)
lines[i] = Html.escapeHtml(lines[i]);
Element pre = document.createElement("pre");
pre.html(TextUtils.join("<br>", lines));
document.body().appendChild(pre);
}
// Draw images // Draw images
Spanned spanned = HtmlHelper.fromDocument(context, document, new Html.ImageGetter() { Spanned spanned = HtmlHelper.fromDocument(context, document, new Html.ImageGetter() {
@Override @Override
@ -4647,7 +4635,6 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
this.language_detection = prefs.getBoolean("language_detection", false); this.language_detection = prefs.getBoolean("language_detection", false);
debug = prefs.getBoolean("debug", false); debug = prefs.getBoolean("debug", false);
this.experiments = prefs.getBoolean("experiments", false);
DiffUtil.ItemCallback<TupleMessageEx> callback = new DiffUtil.ItemCallback<TupleMessageEx>() { DiffUtil.ItemCallback<TupleMessageEx> callback = new DiffUtil.ItemCallback<TupleMessageEx>() {
@Override @Override

@ -272,6 +272,8 @@ public class ApplicationEx extends Application {
} else if (version < 1121) { } else if (version < 1121) {
if (!Helper.isPlayStoreInstall()) if (!Helper.isPlayStoreInstall())
editor.putBoolean("experiments", true); editor.putBoolean("experiments", true);
} else if (version < 1124) {
editor.remove("experiments");
} }
if (version < BuildConfig.VERSION_CODE) if (version < BuildConfig.VERSION_CODE)

@ -314,8 +314,6 @@ public class HtmlHelper {
boolean display_hidden = prefs.getBoolean("display_hidden", false); boolean display_hidden = prefs.getBoolean("display_hidden", false);
boolean disable_tracking = prefs.getBoolean("disable_tracking", true); boolean disable_tracking = prefs.getBoolean("disable_tracking", true);
boolean parse_classes = prefs.getBoolean("parse_classes", false); boolean parse_classes = prefs.getBoolean("parse_classes", false);
boolean experiments = prefs.getBoolean("experiments", false);
// https://chromium.googlesource.com/chromium/blink/+/master/Source/core/css/html.css // https://chromium.googlesource.com/chromium/blink/+/master/Source/core/css/html.css
@ -754,13 +752,13 @@ public class HtmlHelper {
// Subscript/Superscript // Subscript/Superscript
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup
if (!experiments || !view) if (!view)
for (Element subp : document.select("sub,sup")) for (Element subp : document.select("sub,sup"))
subp.tagName("small"); subp.tagName("small");
// Lists // Lists
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li
if (!experiments || !view) { if (!view) {
for (Element li : document.select("li")) { for (Element li : document.select("li")) {
li.tagName("span"); li.tagName("span");
Element parent = li.parent(); Element parent = li.parent();
@ -1779,295 +1777,291 @@ public class HtmlHelper {
static Spanned fromDocument(Context context, @NonNull Document document, @Nullable Html.ImageGetter imageGetter, @Nullable Html.TagHandler tagHandler) { static Spanned fromDocument(Context context, @NonNull Document document, @Nullable Html.ImageGetter imageGetter, @Nullable Html.TagHandler tagHandler) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean experiments = prefs.getBoolean("experiments", false);
boolean debug = prefs.getBoolean("debug", false); boolean debug = prefs.getBoolean("debug", false);
int colorAccent = Helper.resolveColor(context, R.attr.colorAccent); int colorAccent = Helper.resolveColor(context, R.attr.colorAccent);
int dp3 = Helper.dp2pixels(context, 3); int dp3 = Helper.dp2pixels(context, 3);
int dp6 = Helper.dp2pixels(context, 6); int dp6 = Helper.dp2pixels(context, 6);
int dp24 = Helper.dp2pixels(context, 24); int dp24 = Helper.dp2pixels(context, 24);
if (experiments) { // https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements
// https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements NodeTraversor.traverse(new NodeVisitor() {
NodeTraversor.traverse(new NodeVisitor() { private int pre = 0;
private int pre = 0; private Element element;
private Element element; private List<TextNode> block = new ArrayList<>();
private List<TextNode> block = new ArrayList<>();
private String WHITESPACE = " \t\f\u00A0";
private String WHITESPACE = " \t\f\u00A0"; private String WHITESPACE_NL = WHITESPACE + "\r\n";
private String WHITESPACE_NL = WHITESPACE + "\r\n"; private Pattern TRIM_WHITESPACE_NL =
private Pattern TRIM_WHITESPACE_NL = Pattern.compile("[" + WHITESPACE + "]*\\r?\\n[" + WHITESPACE + "]*");
Pattern.compile("[" + WHITESPACE + "]*\\r?\\n[" + WHITESPACE + "]*");
private List<String> BLOCK_START = Collections.unmodifiableList(Arrays.asList(
private List<String> BLOCK_START = Collections.unmodifiableList(Arrays.asList( "body", "blockquote", "h1", "h2", "h3", "h4", "h5", "h6", "li", "ol", "ul", "pre"
"body", "blockquote", "h1", "h2", "h3", "h4", "h5", "h6", "li", "ol", "ul", "pre" ));
)); private List<String> BLOCK_END = Collections.unmodifiableList(Arrays.asList(
private List<String> BLOCK_END = Collections.unmodifiableList(Arrays.asList( "body", "blockquote", "br", "h1", "h2", "h3", "h4", "h5", "h6", "li", "ol", "ul", "pre"
"body", "blockquote", "br", "h1", "h2", "h3", "h4", "h5", "h6", "li", "ol", "ul", "pre" ));
));
@Override
@Override public void head(Node node, int depth) {
public void head(Node node, int depth) { if (node instanceof TextNode) {
if (node instanceof TextNode) { if (pre == 0)
if (pre == 0) block.add((TextNode) node);
block.add((TextNode) node); } else if (node instanceof Element) {
} else if (node instanceof Element) { element = (Element) node;
element = (Element) node; if (BLOCK_START.contains(element.tagName())) {
if (BLOCK_START.contains(element.tagName())) { normalizeText(block);
normalizeText(block); block.clear();
block.clear();
}
if ("pre".equals(element.tagName()))
pre++;
} }
if ("pre".equals(element.tagName()))
pre++;
} }
}
@Override @Override
public void tail(Node node, int depth) { public void tail(Node node, int depth) {
if (node instanceof Element) { if (node instanceof Element) {
element = (Element) node; element = (Element) node;
if (BLOCK_END.contains(element.tagName())) { if (BLOCK_END.contains(element.tagName())) {
normalizeText(block); normalizeText(block);
block.clear(); block.clear();
}
if ("pre".equals(element.tagName()))
pre--;
} }
if ("pre".equals(element.tagName()))
pre--;
} }
}
private void normalizeText(List<TextNode> block) { private void normalizeText(List<TextNode> block) {
// https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace // https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace
TextNode tnode; TextNode tnode;
String text; String text;
for (int i = 0; i < block.size(); ) { for (int i = 0; i < block.size(); ) {
tnode = block.get(i); tnode = block.get(i);
text = tnode.getWholeText(); text = tnode.getWholeText();
// Remove whitespace before/after newlines // Remove whitespace before/after newlines
TRIM_WHITESPACE_NL.matcher(text).replaceAll(" "); TRIM_WHITESPACE_NL.matcher(text).replaceAll(" ");
if (i == 0 || endsWithWhitespace(block.get(i - 1).text())) if (i == 0 || endsWithWhitespace(block.get(i - 1).text()))
while (startsWithWhiteSpace(text)) while (startsWithWhiteSpace(text))
text = text.substring(1); text = text.substring(1);
if (i == block.size() - 1) if (i == block.size() - 1)
while (endsWithWhitespace(text)) while (endsWithWhitespace(text))
text = text.substring(0, text.length() - 1); text = text.substring(0, text.length() - 1);
tnode.text(text); tnode.text(text);
if (TextUtils.isEmpty(text)) if (TextUtils.isEmpty(text))
block.remove(i); block.remove(i);
else else
i++; i++;
} }
if (debug) { if (debug) {
if (block.size() > 0) { if (block.size() > 0) {
block.get(0).text("(" + block.get(0)); block.get(0).text("(" + block.get(0));
block.get(block.size() - 1).text(block.get(block.size() - 1) + ")"); block.get(block.size() - 1).text(block.get(block.size() - 1) + ")");
}
} }
} }
}
boolean startsWithWhiteSpace(String text) { boolean startsWithWhiteSpace(String text) {
int len = text.length(); int len = text.length();
if (len == 0) if (len == 0)
return false; return false;
return WHITESPACE_NL.contains(text.substring(0, 1)); return WHITESPACE_NL.contains(text.substring(0, 1));
} }
boolean endsWithWhitespace(String text) { boolean endsWithWhitespace(String text) {
int len = text.length(); int len = text.length();
if (len == 0) if (len == 0)
return false; return false;
return WHITESPACE_NL.contains(text.substring(len - 1)); return WHITESPACE_NL.contains(text.substring(len - 1));
} }
}, document.body()); }, document.body());
// https://developer.android.com/guide/topics/text/spans // https://developer.android.com/guide/topics/text/spans
SpannableStringBuilder ssb = new SpannableStringBuilder(); SpannableStringBuilder ssb = new SpannableStringBuilder();
NodeTraversor.traverse(new NodeVisitor() { NodeTraversor.traverse(new NodeVisitor() {
private Element element; private Element element;
private TextNode tnode; private TextNode tnode;
@Override @Override
public void head(Node node, int depth) { public void head(Node node, int depth) {
if (node instanceof Element) { if (node instanceof Element) {
element = (Element) node; element = (Element) node;
element.attr("start-index", Integer.toString(ssb.length())); element.attr("start-index", Integer.toString(ssb.length()));
if (debug) if (debug)
ssb.append("[" + element.tagName() + "]"); ssb.append("[" + element.tagName() + "]");
} else if (node instanceof TextNode) { } else if (node instanceof TextNode) {
tnode = (TextNode) node; tnode = (TextNode) node;
ssb.append(tnode.text()); ssb.append(tnode.text());
}
} }
}
@Override @Override
public void tail(Node node, int depth) { public void tail(Node node, int depth) {
if (node instanceof Element) { if (node instanceof Element) {
element = (Element) node; element = (Element) node;
int start = Integer.parseInt(element.attr("start-index")); int start = Integer.parseInt(element.attr("start-index"));
if (debug) if (debug)
ssb.append("[/" + element.tagName() + "]"); ssb.append("[/" + element.tagName() + "]");
switch (element.tagName()) { switch (element.tagName()) {
case "a": case "a":
String href = element.attr("href"); String href = element.attr("href");
ssb.setSpan(new URLSpan(href), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new URLSpan(href), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "body": case "body":
// Do nothing // Do nothing
break; break;
case "big": case "big":
ssb.setSpan(new RelativeSizeSpan(FONT_LARGE), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new RelativeSizeSpan(FONT_LARGE), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "blockquote": case "blockquote":
ssb.setSpan(new QuoteSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new QuoteSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "br": case "br":
newline(ssb.length()); newline(ssb.length());
break; break;
case "i": case "i":
case "em": case "em":
ssb.setSpan(new StyleSpan(Typeface.ITALIC), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new StyleSpan(Typeface.ITALIC), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "font": case "font":
// Do nothing // Do nothing
break; break;
case "h1": case "h1":
case "h2": case "h2":
case "h3": case "h3":
case "h4": case "h4":
case "h5": case "h5":
case "h6": case "h6":
int level = element.tagName().charAt(1) - '1'; int level = element.tagName().charAt(1) - '1';
ssb.setSpan(new RelativeSizeSpan(HEADING_SIZES[level]), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new RelativeSizeSpan(HEADING_SIZES[level]), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.setSpan(new StyleSpan(Typeface.BOLD), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new StyleSpan(Typeface.BOLD), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
newline(start); newline(start);
newline(ssb.length()); newline(ssb.length());
break; break;
case "img": case "img":
String src = element.attr("src"); String src = element.attr("src");
Drawable d = (imageGetter == null Drawable d = (imageGetter == null
? context.getDrawable(R.drawable.baseline_broken_image_24) ? context.getDrawable(R.drawable.baseline_broken_image_24)
: imageGetter.getDrawable(src)); : imageGetter.getDrawable(src));
ssb.insert(start, "\uFFFC"); // Object replacement character ssb.insert(start, "\uFFFC"); // Object replacement character
ssb.setSpan(new ImageSpan(d, src), start, start + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new ImageSpan(d, src), start, start + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "li": case "li":
newline(ssb.length()); newline(ssb.length());
Element parent = element.parent(); Element parent = element.parent();
if (parent == null || "ul".equals(parent.tagName())) if (parent == null || "ul".equals(parent.tagName()))
// TODO BulletSpanCompat // TODO BulletSpanCompat
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
ssb.setSpan(new BulletSpan(dp6, colorAccent), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new BulletSpan(dp6, colorAccent), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
else else
ssb.setSpan(new BulletSpan(dp6, colorAccent, dp3), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new BulletSpan(dp6, colorAccent, dp3), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
else { else {
int index = 0; int index = 0;
for (Node child : parent.childNodes()) { for (Node child : parent.childNodes()) {
if (child instanceof Element) { if (child instanceof Element) {
index++; index++;
if (child == element) if (child == element)
break; break;
}
} }
ssb.setSpan(new NumberSpan(dp6, colorAccent, index), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
break;
case "ol":
case "ul":
int llevel = 0;
Element lparent = element.parent();
while (lparent != null) {
if (lparent.tagName().equals(element.tagName()))
llevel++;
lparent = lparent.parent();
} }
if (llevel > 0) ssb.setSpan(new NumberSpan(dp6, colorAccent, index), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.setSpan(new LeadingMarginSpan.Standard(llevel * dp24), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); }
newline(start); break;
newline(ssb.length()); case "ol":
break; case "ul":
case "pre": int llevel = 0;
// Do nothing Element lparent = element.parent();
break; while (lparent != null) {
case "small": if (lparent.tagName().equals(element.tagName()))
ssb.setSpan(new RelativeSizeSpan(FONT_SMALL), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); llevel++;
break; lparent = lparent.parent();
case "span": }
// Do nothing if (llevel > 0)
break; ssb.setSpan(new LeadingMarginSpan.Standard(llevel * dp24), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
case "sub": newline(start);
ssb.setSpan(new SubscriptSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); newline(ssb.length());
ssb.setSpan(new RelativeSizeSpan(FONT_SMALL), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); break;
break; case "pre":
case "sup": // Do nothing
ssb.setSpan(new SuperscriptSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); break;
ssb.setSpan(new RelativeSizeSpan(FONT_SMALL), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); case "small":
break; ssb.setSpan(new RelativeSizeSpan(FONT_SMALL), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
case "b": break;
case "strong": case "span":
ssb.setSpan(new StyleSpan(Typeface.BOLD), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // Do nothing
break; break;
case "s": case "sub":
case "del": ssb.setSpan(new SubscriptSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.setSpan(new StrikethroughSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new RelativeSizeSpan(FONT_SMALL), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "tt": case "sup":
ssb.setSpan(new TypefaceSpan("monospace"), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new SuperscriptSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; ssb.setSpan(new RelativeSizeSpan(FONT_SMALL), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
case "u": break;
ssb.setSpan(new UnderlineSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); case "b":
break; case "strong":
default: ssb.setSpan(new StyleSpan(Typeface.BOLD), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Log.e("Unknown tag=" + element.tagName()); break;
} case "s":
case "del":
ssb.setSpan(new StrikethroughSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break;
case "tt":
ssb.setSpan(new TypefaceSpan("monospace"), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break;
case "u":
ssb.setSpan(new UnderlineSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break;
default:
Log.e("Unknown tag=" + element.tagName());
}
String style = element.attr("style"); String style = element.attr("style");
if (!TextUtils.isEmpty(style)) { if (!TextUtils.isEmpty(style)) {
String[] params = style.split(";"); String[] params = style.split(";");
for (String param : params) { for (String param : params) {
int semi = param.indexOf(":"); int semi = param.indexOf(":");
if (semi < 0) if (semi < 0)
continue; continue;
String key = param.substring(0, semi); String key = param.substring(0, semi);
String value = param.substring(semi + 1); String value = param.substring(semi + 1);
switch (key) { switch (key) {
case "color": case "color":
int color = Integer.parseInt(value.substring(1), 16) | 0xFF000000; int color = Integer.parseInt(value.substring(1), 16) | 0xFF000000;
ssb.setSpan(new ForegroundColorSpan(color), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new ForegroundColorSpan(color), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "text-decoration": case "text-decoration":
if ("line-through".equals(value)) if ("line-through".equals(value))
ssb.setSpan(new StrikethroughSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new StrikethroughSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
}
} }
} }
} }
} }
}
private void newline(int index) { private void newline(int index) {
int len = ssb.length(); int len = ssb.length();
if (len > 2 && if (len > 2 &&
ssb.charAt(len - 1) == '\n' && ssb.charAt(len - 1) == '\n' &&
ssb.charAt(len - 2) == '\n') ssb.charAt(len - 2) == '\n')
return; return;
ssb.insert(index, "\n"); ssb.insert(index, "\n");
} }
}, document.body()); }, document.body());
if (debug) if (debug)
for (int i = ssb.length() - 1; i >= 0; i--) for (int i = ssb.length() - 1; i >= 0; i--)
if (ssb.charAt(i) == '\n') if (ssb.charAt(i) == '\n')
ssb.insert(i, "|"); ssb.insert(i, "|");
return reverseSpans(ssb); return reverseSpans(ssb);
} else
return fromHtml(document.html(), imageGetter, null);
} }
static Spanned fromHtml(@NonNull String html) { static Spanned fromHtml(@NonNull String html) {

Loading…
Cancel
Save