package com.moez.QKSMS.common.utils; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.provider.Browser; import android.text.Spannable; import android.text.SpannableString; import android.text.style.CharacterStyle; import android.text.style.ClickableSpan; import android.text.style.URLSpan; import android.text.util.Linkify; import android.text.util.Linkify.TransformFilter; import android.util.Log; import android.view.View; import android.widget.TextView; import java.util.regex.Pattern; /** * Shamelessly stolen from * <a href="https://raw.githubusercontent.com/guardianproject/ChatSecureAndroid/master/src/info/guardianproject/util/LinkifyHelper.java">ChatSecure's implementation</a> */ public class LinkifyUtils { private static Pattern bitcoin = Pattern.compile("bitcoin:[1-9a-km-zA-HJ-NP-Z]{27,34}(\\?[a-zA-Z0-9$\\-_.+!*'(),%:;@&=]*)?"); private static Pattern geo = Pattern.compile("geo:[-0-9.]+,[-0-9.]+[^ \t\n\"\':]*"); private static Pattern market = Pattern.compile("market://[^ \t\n\"\':,<>]+"); private static Pattern openpgp4fpr = Pattern.compile("openpgp4fpr:[A-Za-z0-9]{8,40}"); private static Pattern xmpp = Pattern.compile("xmpp:[^ \t\n\"\':,<>]+"); private static Pattern twitterHandle = Pattern.compile("@([A-Za-z0-9_-]+)"); private static Pattern hashtag = Pattern.compile("#([A-Za-z0-9_-]+)"); static TransformFilter returnMatchFilter = (match, url) -> match.group(1); /* Right now, if there is no app to handle */ public static void addLinks(TextView text) { Linkify.addLinks(text, Linkify.ALL); Linkify.addLinks(text, geo, null); Linkify.addLinks(text, market, null); Linkify.addLinks(text, openpgp4fpr, null); Linkify.addLinks(text, bitcoin, null); Linkify.addLinks(text, xmpp, null); Linkify.addLinks(text, twitterHandle, "https://twitter.com/", null, returnMatchFilter); Linkify.addLinks(text, hashtag, "https://twitter.com/hashtag/", null, returnMatchFilter); text.setText(replaceAll(text.getText(), URLSpan.class, new URLSpanConverter())); } /** * Do not create this static utility class. */ private LinkifyUtils() { } // thanks to @commonsware https://stackoverflow.com/a/11417498 public static <A extends CharacterStyle, B extends CharacterStyle> Spannable replaceAll( CharSequence original, Class<A> sourceType, SpanConverter<A, B> converter) { SpannableString result = new SpannableString(original); A[] spans = result.getSpans(0, result.length(), sourceType); for (A span : spans) { int start = result.getSpanStart(span); int end = result.getSpanEnd(span); int flags = result.getSpanFlags(span); result.removeSpan(span); result.setSpan(converter.convert(span), start, end, flags); } return (result); } private interface SpanConverter<A extends CharacterStyle, B extends CharacterStyle> { B convert(A span); } /** * This trickery is needed in order to have clickable links that open things * in a new {@code Task} rather than in ChatSecure's {@code Task.} Thanks to @commonsware * https://stackoverflow.com/a/11417498 */ private static class NewTaskUrlSpan extends ClickableSpan { private String urlString; NewTaskUrlSpan(String urlString) { this.urlString = urlString; } @Override public void onClick(View widget) { try { Uri uri = Uri.parse(urlString); Context context = widget.getContext(); Intent intent = new Intent(Intent.ACTION_VIEW, uri); intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } catch (ActivityNotFoundException ignored) { Log.e("LinkifyUtils", "", ignored); // We should (or shouldn't) explain that no app can handle this intent ? } } } private static class URLSpanConverter implements LinkifyUtils.SpanConverter<URLSpan, ClickableSpan> { @Override public NewTaskUrlSpan convert(URLSpan span) { return (new NewTaskUrlSpan(span.getURL())); } } }