package com.door43.translationstudio.rendering; import android.text.TextUtils; import android.webkit.WebView; import android.webkit.WebViewClient; import com.door43.translationstudio.spannables.ArticleLinkSpan; import com.door43.translationstudio.spannables.PassageLinkSpan; import com.door43.translationstudio.spannables.Span; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Handles rendering of internal links to html anchors */ public class LinkToHtmlRenderer extends RenderingEngine { public static final String SCHEME_TA = "ta"; public static final String SCHEME_CHUNK = "chunk"; private final OnPreprocessLink preprocessCallback; /** * * @param preprocessor called to determine if link should be rendered */ public LinkToHtmlRenderer(OnPreprocessLink preprocessor) { preprocessCallback = preprocessor; } @Override public CharSequence render(CharSequence in) { CharSequence out = in; out = renderTranslationAcademyAddress(out); out = renderTranslationAcademyLink(out); out = renderPassageLink(out); return out; } /** * Renders links to other passages in the project * Example [[:en:bible:notes:gen:01:03|1:5]] * @param in * @return */ private CharSequence renderPassageLink(CharSequence in) { return renderLink(in, SCHEME_CHUNK, PassageLinkSpan.PATTERN, new OnCreateLink() { @Override public Span onCreate(Matcher matcher) { return new PassageLinkSpan(matcher.group(3), matcher.group(2)); } }); } /** * Renders addresses to translation academy pages as html * Example [[en:ta:vol1:translate:translate_unknown|How to Translate Unknowns]] * @param in * @return */ public CharSequence renderTranslationAcademyAddress(CharSequence in) { return renderLink(in, SCHEME_TA, ArticleLinkSpan.ADDRESS_PATTERN, new OnCreateLink() { @Override public Span onCreate(Matcher matcher) { String title = matcher.group(4); if(title == null) { title = matcher.group(0); } return ArticleLinkSpan.parse(title, matcher.group(2)); } }); } /** * Renders links to translation academy pages as html * Example <a href="/en/ta/vol1/translate/figs_intro" title="en:ta:vol1:translate:figs_intro">Figures of Speech</a> * @param in * @return */ public CharSequence renderTranslationAcademyLink(CharSequence in) { return renderLink(in, SCHEME_TA, ArticleLinkSpan.LINK_PATTERN, new OnCreateLink() { @Override public Span onCreate(Matcher matcher) { String title = matcher.group(6); if(title == null) { title = matcher.group(0); } return ArticleLinkSpan.parse(title, matcher.group(3).replace("/", ":")); } }); } /** * A generic rendering method for rendering content links as html * * @param in * @param scheme * @param pattern * @param callback @return */ private CharSequence renderLink(CharSequence in, String scheme, Pattern pattern, OnCreateLink callback) { CharSequence out = ""; Matcher matcher = pattern.matcher(in); int lastIndex = 0; while(matcher.find()) { if(isStopped()) return in; Span link = callback.onCreate(matcher); if(link != null) { if (preprocessCallback == null || preprocessCallback.onPreprocess(link)) { // render clickable link CharSequence title = link.getHumanReadable(); if(title == null || title.toString().isEmpty()) { title = link.getMachineReadable(); } String htmlLink = "<a href=\"" + scheme + "://" + link.getMachineReadable() + "\">" + title + "</a>"; out = TextUtils.concat(out, in.subSequence(lastIndex, matcher.start()), htmlLink); } else { // render as plain text out = TextUtils.concat(out, in.subSequence(lastIndex, matcher.start()), link.getHumanReadable()); } } else { // ignore link out = TextUtils.concat(out, in.subSequence(lastIndex, matcher.end())); } lastIndex = matcher.end(); } out = TextUtils.concat(out, in.subSequence(lastIndex, in.length())); return out; } private interface OnCreateLink { Span onCreate(Matcher matcher); } /** * Used to identify which links to render */ public interface OnPreprocessLink { boolean onPreprocess(Span span); } /** * Provides custom link handling for webviews */ public static abstract class CustomWebViewClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { String postfix = "://"; if(url != null) { if (url.startsWith(SCHEME_CHUNK + postfix)) { onOverriddenLinkClick(view, url, new PassageLinkSpan("", url.substring((SCHEME_CHUNK + postfix).length()))); return true; } else if (url.startsWith(SCHEME_TA + postfix)) { onOverriddenLinkClick(view, url, ArticleLinkSpan.parse(url.substring((SCHEME_TA + postfix).length()))); return true; } } onLinkClick(view, url); return true; } /** * Called when a url was successfully overridden * @param view * @param url * @param span */ public abstract void onOverriddenLinkClick(WebView view, String url, Span span); /** * Called when a normal link is clicked * @param view * @param url */ public abstract void onLinkClick(WebView view, String url); } }