package com.fastaccess.provider.timeline; import android.content.Context; import android.graphics.Point; import android.net.Uri; import android.support.annotation.ColorInt; import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; import android.view.WindowManager; import android.widget.TextView; import com.fastaccess.R; import com.fastaccess.helper.AppHelper; import com.fastaccess.helper.ViewHelper; import com.fastaccess.provider.scheme.SchemeParser; import com.fastaccess.provider.timeline.handler.BetterLinkMovementExtended; import com.fastaccess.provider.timeline.handler.DrawableHandler; import com.fastaccess.provider.timeline.handler.ItalicHandler; import com.fastaccess.provider.timeline.handler.LinkHandler; import com.fastaccess.provider.timeline.handler.ListsHandler; import com.fastaccess.provider.timeline.handler.MarginHandler; import com.fastaccess.provider.timeline.handler.PreTagHandler; import com.fastaccess.provider.timeline.handler.QouteHandler; import com.fastaccess.provider.timeline.handler.StrikethroughHandler; import com.fastaccess.provider.timeline.handler.SubScriptHandler; import com.fastaccess.provider.timeline.handler.SuperScriptHandler; import com.fastaccess.provider.timeline.handler.TableHandler; import com.fastaccess.provider.timeline.handler.UnderlineHandler; import net.nightwhistler.htmlspanner.HtmlSpanner; import net.nightwhistler.htmlspanner.handlers.BoldHandler; /** * Created by Kosh on 21 Apr 2017, 11:24 PM */ public class HtmlHelper { public static void htmlIntoTextView(@NonNull TextView textView, @NonNull String html) { registerClickEvent(textView); textView.setText(initHtml(textView).fromHtml(format(html).toString())); } private static void registerClickEvent(@NonNull TextView textView) { BetterLinkMovementExtended betterLinkMovementMethod = BetterLinkMovementExtended.linkifyHtml(textView); betterLinkMovementMethod.setOnLinkClickListener((view, url) -> { SchemeParser.launchUri(view.getContext(), Uri.parse(url)); return true; }); } private static HtmlSpanner initHtml(@NonNull TextView textView) { boolean isDark = AppHelper.isNightMode(textView.getResources()); @ColorInt int windowBackground = isDark ? ViewHelper.getWindowBackground(textView.getContext()) : ContextCompat.getColor(textView.getContext(), R.color.light_patch_ref_color); HtmlSpanner mySpanner = new HtmlSpanner(); mySpanner.setStripExtraWhiteSpace(true); mySpanner.registerHandler("pre", new PreTagHandler(windowBackground, true, isDark)); mySpanner.registerHandler("code", new PreTagHandler(windowBackground, false, isDark)); mySpanner.registerHandler("img", new DrawableHandler(textView)); mySpanner.registerHandler("g-emoji", new DrawableHandler(textView)); mySpanner.registerHandler("blockquote", new QouteHandler(windowBackground)); mySpanner.registerHandler("b", new BoldHandler()); mySpanner.registerHandler("strong", new BoldHandler()); mySpanner.registerHandler("i", new ItalicHandler()); mySpanner.registerHandler("em", new ItalicHandler()); mySpanner.registerHandler("ul", new MarginHandler()); mySpanner.registerHandler("ol", new MarginHandler()); mySpanner.registerHandler("li", new ListsHandler()); mySpanner.registerHandler("u", new UnderlineHandler()); mySpanner.registerHandler("strike", new StrikethroughHandler()); mySpanner.registerHandler("ins", new UnderlineHandler()); mySpanner.registerHandler("del", new StrikethroughHandler()); mySpanner.registerHandler("sub", new SubScriptHandler()); mySpanner.registerHandler("sup", new SuperScriptHandler()); mySpanner.registerHandler("a", new LinkHandler()); TableHandler tableHandler = new TableHandler(); tableHandler.setTextColor(ViewHelper.generateTextColor(windowBackground)); WindowManager windowManager = (WindowManager) textView.getContext().getSystemService(Context.WINDOW_SERVICE); Point point = new Point(); windowManager.getDefaultDisplay().getRealSize(point); tableHandler.setTableWidth((int) (point.x / 1.2)); tableHandler.setTextSize(18.0F); mySpanner.registerHandler("table", tableHandler); return mySpanner; } private static final String TAG_ROOT = "githubroot"; private static final String ROOT_START = '<' + TAG_ROOT + '>'; private static final String ROOT_END = "</" + TAG_ROOT + '>'; private static final String TOGGLE_START = "<span class=\"email-hidden-toggle\">"; private static final String TOGGLE_END = "</span>"; private static final String REPLY_START = "<div class=\"email-quoted-reply\">"; private static final String REPLY_END = "</div>"; private static final String SIGNATURE_START = "<div class=\"email-signature-reply\">"; private static final String SIGNATURE_END = "</div>"; private static final String EMAIL_START = "<div class=\"email-fragment\">"; private static final String EMAIL_END = "</div>"; private static final String HIDDEN_REPLY_START = "<div class=\"email-hidden-reply\" style=\" display:none\">"; private static final String HIDDEN_REPLY_END = "</div>"; private static final String BREAK = "<br>"; private static final String PARAGRAPH_START = "<p>"; private static final String PARAGRAPH_END = "</p>"; private static final String BLOCKQUOTE_START = "<blockquote>"; private static final String BLOCKQUOTE_END = "</blockquote>"; //https://github.com/k0shk0sh/GitHubSdk/blob/master/library/src/main/java/com/meisolsson/githubsdk/core/HtmlUtils.java @NonNull public static CharSequence format(final String html) { if (html == null || html.length() == 0) return ""; StringBuilder formatted = new StringBuilder(html); strip(formatted, TOGGLE_START, TOGGLE_END); strip(formatted, SIGNATURE_START, SIGNATURE_END); replace(formatted, REPLY_START, REPLY_END, BLOCKQUOTE_START, BLOCKQUOTE_END); strip(formatted, HIDDEN_REPLY_START, HIDDEN_REPLY_END); if (replace(formatted, PARAGRAPH_START, BREAK)) replace(formatted, PARAGRAPH_END, BREAK); formatEmailFragments(formatted); trim(formatted); formatted.insert(0, ROOT_START); formatted.append(ROOT_END); return formatted; } private static void strip(final StringBuilder input, final String prefix, final String suffix) { int start = input.indexOf(prefix); while (start != -1) { int end = input.indexOf(suffix, start + prefix.length()); if (end == -1) end = input.length(); input.delete(start, end + suffix.length()); start = input.indexOf(prefix, start); } } private static boolean replace(final StringBuilder input, final String from, final String to) { int start = input.indexOf(from); if (start == -1) return false; final int fromLength = from.length(); final int toLength = to.length(); while (start != -1) { input.replace(start, start + fromLength, to); start = input.indexOf(from, start + toLength); } return true; } private static void replace(final StringBuilder input, final String fromStart, final String fromEnd, final String toStart, final String toEnd) { int start = input.indexOf(fromStart); if (start == -1) return; final int fromStartLength = fromStart.length(); final int fromEndLength = fromEnd.length(); final int toStartLength = toStart.length(); while (start != -1) { input.replace(start, start + fromStartLength, toStart); int end = input.indexOf(fromEnd, start + toStartLength); if (end != -1) input.replace(end, end + fromEndLength, toEnd); start = input.indexOf(fromStart); } } private static void formatEmailFragments(final StringBuilder input) { int emailStart = input.indexOf(EMAIL_START); int breakAdvance = BREAK.length() - 1; while (emailStart != -1) { int startLength = EMAIL_START.length(); int emailEnd = input.indexOf(EMAIL_END, emailStart + startLength); if (emailEnd == -1) break; input.delete(emailEnd, emailEnd + EMAIL_END.length()); input.delete(emailStart, emailStart + startLength); int fullEmail = emailEnd - startLength; for (int i = emailStart; i < fullEmail; i++) if (input.charAt(i) == '\n') { input.deleteCharAt(i); input.insert(i, BREAK); i += breakAdvance; fullEmail += breakAdvance; } emailStart = input.indexOf(EMAIL_START, fullEmail); } } private static void trim(final StringBuilder input) { int length = input.length(); int breakLength = BREAK.length(); while (length > 0) { if (input.indexOf(BREAK) == 0) input.delete(0, breakLength); else if (length >= breakLength && input.lastIndexOf(BREAK) == length - breakLength) input.delete(length - breakLength, length); else if (Character.isWhitespace(input.charAt(0))) input.deleteCharAt(0); else if (Character.isWhitespace(input.charAt(length - 1))) input.deleteCharAt(length - 1); else break; length = input.length(); } } }