package com.ubergeek42.WeechatAndroid.utils;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.style.URLSpan;
import android.view.View;
import android.widget.Toast;
import com.ubergeek42.WeechatAndroid.R;
import com.ubergeek42.WeechatAndroid.relay.Line;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Our own Linkifier
* Rationale: allow custom custom URLs as well URLs starting with www. with less fiddling;
* also use non-colored URLSpans so that we don't have to recreate them every time
*/
public class Linkify {
// pattern will always find urls starting with protocol, the only exception being "www."
// in this case, prepend "http://" to url.
public static void linkify (@NonNull Spannable s) {
Matcher m = URL.matcher(s);
while (m.find()) {
String url = m.group(0);
if (url.startsWith("www."))
url = "http://" + url;
s.setSpan(new URLSpan2(url), m.start(), m.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
// an url span that doesn't change the color of the link
// also it checks if the text line associated with it has clickDisable set by
// onItemLongClick from ChatLinesAdapter, which prevents unwanted clicks on long clicks
// also make sure we don't crash if nothing can handle our intent
private static class URLSpan2 extends URLSpan {
public URLSpan2(@NonNull String url) {super(url);}
@Override public void updateDrawState(@NonNull TextPaint ds) {
ds.setUnderlineText(true);
}
@Override public void onClick(@NonNull View widget) {
Line line = (Line) widget.getTag();
if (line.clickDisabled)
line.clickDisabled = false;
else {
// don't call super because super will open urls in the same tab
Context context = widget.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getURL()));
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
CharSequence text = context.getString(R.string.linkify_error_no_activity, getURL());
Toast.makeText(widget.getContext(), text, Toast.LENGTH_SHORT).show();
}
}
}
}
final private static String IRIC = "[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]";
final private static String GLTDC = "[a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]";
final private static Pattern URL = Pattern.compile(
// url must be preceded by a word boundary
"\\b" +
// protocol:// or www.
"(?:[A-z]+://|www\\.)" +
// optional user:pass at
"(?:\\S+(?::\\S*)?@)?" +
"(?:" +
// ip address (+ some exceptions)
"(?!10(?:\\.\\d{1,3}){3})" +
"(?!127(?:\\.\\d{1,3}){3})" +
"(?!169\\.254(?:\\.\\d{1,3}){2})" +
"(?!192\\.168(?:\\.\\d{1,3}){2})" +
"(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" +
"(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
"(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
"(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
"|" +
// domain name (a.b.c.com)
"(?:" + IRIC + "+(?:-" + IRIC + "+)*\\.)+" + // (\w+(-\w+)*\.)+ a. a-b. a-b.a-b.
GLTDC + "{2,63}" + // (\w){2,63} com ninja
")" +
// port?
"(?::\\d{2,5})?" +
// & the rest
"(?:" +
"\\.?/" +
"(?:" +
// hello(world) in hello(world))
"(?:" +
"[^\\s(]*" +
"\\(" +
"[^\\s)]+" +
"\\)" +
")+" +
"[^\\s)]*?" +
"|" +
// any string (non-greedy!)
"\\S*?" +
")" +
")?" +
// url must be directly followed by
"(?=" +
// some possible punctuation
// AND space or end of string
"[\\])>,.!?:\"”]*" +
"(?:\\s|$)" +
")"
, Pattern.CASE_INSENSITIVE | Pattern.COMMENTS);
}