Comment issue #366 - Make text clickable
This commit is contained in:
parent
60a1b2bfa4
commit
4d3c94c7fd
|
@ -115,6 +115,7 @@ public class SpannableHelper {
|
||||||
HashMap<String, String> urlDetails = new HashMap<>();
|
HashMap<String, String> urlDetails = new HashMap<>();
|
||||||
if (convertHtml) {
|
if (convertHtml) {
|
||||||
Matcher matcherALink = Helper.aLink.matcher(text);
|
Matcher matcherALink = Helper.aLink.matcher(text);
|
||||||
|
|
||||||
//We stock details
|
//We stock details
|
||||||
while (matcherALink.find()) {
|
while (matcherALink.find()) {
|
||||||
String urlText = matcherALink.group(3);
|
String urlText = matcherALink.group(3);
|
||||||
|
@ -140,6 +141,7 @@ public class SpannableHelper {
|
||||||
interaction(context, content, mentionList);
|
interaction(context, content, mentionList);
|
||||||
//Make all links
|
//Make all links
|
||||||
linkify(context, content, urlDetails);
|
linkify(context, content, urlDetails);
|
||||||
|
linkifyURL(context, content, urlDetails);
|
||||||
} else {
|
} else {
|
||||||
content = new SpannableStringBuilder(text);
|
content = new SpannableStringBuilder(text);
|
||||||
}
|
}
|
||||||
|
@ -185,6 +187,8 @@ public class SpannableHelper {
|
||||||
Matcher matcherLink = urlPattern.matcher(content);
|
Matcher matcherLink = urlPattern.matcher(content);
|
||||||
|
|
||||||
int offSetTruncate = 0;
|
int offSetTruncate = 0;
|
||||||
|
|
||||||
|
|
||||||
while (matcherLink.find()) {
|
while (matcherLink.find()) {
|
||||||
int matchStart = matcherLink.start() - offSetTruncate;
|
int matchStart = matcherLink.start() - offSetTruncate;
|
||||||
int matchEnd = matchStart + matcherLink.group().length();
|
int matchEnd = matchStart + matcherLink.group().length();
|
||||||
|
@ -433,6 +437,229 @@ public class SpannableHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void linkifyURL(Context context, SpannableStringBuilder content, HashMap<String, String> urlDetails) {
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> entry : urlDetails.entrySet()) {
|
||||||
|
String value = entry.getValue();
|
||||||
|
SpannableString contentUrl;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||||
|
contentUrl = new SpannableString(Html.fromHtml(value, Html.FROM_HTML_MODE_LEGACY));
|
||||||
|
else
|
||||||
|
contentUrl = new SpannableString(Html.fromHtml(value));
|
||||||
|
|
||||||
|
Pattern word = Pattern.compile(contentUrl.toString());
|
||||||
|
Matcher matcherLink = word.matcher(content);
|
||||||
|
while (matcherLink.find()) {
|
||||||
|
String url = entry.getKey();
|
||||||
|
int matchStart = matcherLink.start();
|
||||||
|
int matchEnd = matchStart + matcherLink.group().length();
|
||||||
|
if (matchEnd > content.toString().length()) {
|
||||||
|
matchEnd = content.toString().length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.toString().length() < matchEnd || matchStart < 0 || matchStart > matchEnd) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (matchEnd <= content.length()) {
|
||||||
|
content.setSpan(new LongClickableSpan() {
|
||||||
|
@Override
|
||||||
|
public void onLongClick(View view) {
|
||||||
|
Context mContext = view.getContext();
|
||||||
|
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext, Helper.dialogStyle());
|
||||||
|
PopupLinksBinding popupLinksBinding = PopupLinksBinding.inflate(LayoutInflater.from(context));
|
||||||
|
dialogBuilder.setView(popupLinksBinding.getRoot());
|
||||||
|
AlertDialog alertDialog = dialogBuilder.create();
|
||||||
|
alertDialog.show();
|
||||||
|
String finalURl = url;
|
||||||
|
if (urlDetails.containsValue(url)) {
|
||||||
|
finalURl = Helper.getKeyByValue(urlDetails, url);
|
||||||
|
}
|
||||||
|
String finalURl1 = finalURl;
|
||||||
|
popupLinksBinding.displayFullLink.setOnClickListener(v -> {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(mContext, Helper.dialogStyle());
|
||||||
|
builder.setMessage(finalURl1);
|
||||||
|
builder.setTitle(context.getString(R.string.display_full_link));
|
||||||
|
builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss())
|
||||||
|
.show();
|
||||||
|
alertDialog.dismiss();
|
||||||
|
});
|
||||||
|
popupLinksBinding.shareLink.setOnClickListener(v -> {
|
||||||
|
Intent sendIntent = new Intent(Intent.ACTION_SEND);
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_TEXT, finalURl1);
|
||||||
|
sendIntent.setType("text/plain");
|
||||||
|
sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
Intent intentChooser = Intent.createChooser(sendIntent, context.getString(R.string.share_with));
|
||||||
|
intentChooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
context.startActivity(intentChooser);
|
||||||
|
alertDialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
popupLinksBinding.openOtherApp.setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(finalURl1));
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
try {
|
||||||
|
context.startActivity(intent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
alertDialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
popupLinksBinding.copyLink.setOnClickListener(v -> {
|
||||||
|
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, finalURl1);
|
||||||
|
if (clipboard != null) {
|
||||||
|
clipboard.setPrimaryClip(clip);
|
||||||
|
Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
alertDialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
popupLinksBinding.checkRedirect.setOnClickListener(v -> {
|
||||||
|
try {
|
||||||
|
|
||||||
|
URL finalUrlCheck = new URL(finalURl1);
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
String redirect = null;
|
||||||
|
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) finalUrlCheck.openConnection();
|
||||||
|
httpsURLConnection.setConnectTimeout(10 * 1000);
|
||||||
|
httpsURLConnection.setRequestProperty("http.keepAlive", "false");
|
||||||
|
httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT);
|
||||||
|
httpsURLConnection.setRequestMethod("HEAD");
|
||||||
|
httpsURLConnection.setInstanceFollowRedirects(false);
|
||||||
|
if (httpsURLConnection.getResponseCode() == 301 || httpsURLConnection.getResponseCode() == 302) {
|
||||||
|
Map<String, List<String>> map = httpsURLConnection.getHeaderFields();
|
||||||
|
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
|
||||||
|
if (entry.toString().toLowerCase().startsWith("location")) {
|
||||||
|
Matcher matcher = urlPattern.matcher(entry.toString());
|
||||||
|
if (matcher.find()) {
|
||||||
|
redirect = matcher.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
httpsURLConnection.getInputStream().close();
|
||||||
|
if (redirect != null && finalURl1 != null && redirect.compareTo(finalURl1) != 0) {
|
||||||
|
URL redirectURL = new URL(redirect);
|
||||||
|
String host = redirectURL.getHost();
|
||||||
|
String protocol = redirectURL.getProtocol();
|
||||||
|
if (protocol == null || host == null) {
|
||||||
|
redirect = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Handler mainHandler = new Handler(context.getMainLooper());
|
||||||
|
String finalRedirect = redirect;
|
||||||
|
Runnable myRunnable = () -> {
|
||||||
|
AlertDialog.Builder builder1 = new AlertDialog.Builder(view.getContext(), Helper.dialogStyle());
|
||||||
|
if (finalRedirect != null) {
|
||||||
|
builder1.setMessage(context.getString(R.string.redirect_detected, finalURl1, finalRedirect));
|
||||||
|
builder1.setNegativeButton(R.string.copy_link, (dialog, which) -> {
|
||||||
|
ClipboardManager clipboard1 = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
ClipData clip1 = ClipData.newPlainText(Helper.CLIP_BOARD, finalRedirect);
|
||||||
|
if (clipboard1 != null) {
|
||||||
|
clipboard1.setPrimaryClip(clip1);
|
||||||
|
Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
builder1.setNeutralButton(R.string.share_link, (dialog, which) -> {
|
||||||
|
Intent sendIntent1 = new Intent(Intent.ACTION_SEND);
|
||||||
|
sendIntent1.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
|
||||||
|
sendIntent1.putExtra(Intent.EXTRA_TEXT, finalURl1);
|
||||||
|
sendIntent1.setType("text/plain");
|
||||||
|
context.startActivity(Intent.createChooser(sendIntent1, context.getString(R.string.share_with)));
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
builder1.setMessage(R.string.no_redirect);
|
||||||
|
}
|
||||||
|
builder1.setTitle(context.getString(R.string.check_redirect));
|
||||||
|
builder1.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss())
|
||||||
|
.show();
|
||||||
|
|
||||||
|
};
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}).start();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
alertDialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(@NonNull View textView) {
|
||||||
|
String finalURl = url;
|
||||||
|
if (urlDetails.containsValue(url)) {
|
||||||
|
finalURl = Helper.getKeyByValue(urlDetails, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
textView.setTag(CLICKABLE_SPAN);
|
||||||
|
Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$");
|
||||||
|
Matcher matcherLink = null;
|
||||||
|
if (finalURl != null) {
|
||||||
|
matcherLink = link.matcher(finalURl);
|
||||||
|
}
|
||||||
|
if (finalURl != null && matcherLink.find() && !finalURl.contains("medium.com")) {
|
||||||
|
if (matcherLink.group(3) != null && Objects.requireNonNull(matcherLink.group(3)).length() > 0) { //It's a toot
|
||||||
|
CrossActionHelper.fetchRemoteStatus(context, currentAccount, finalURl, new CrossActionHelper.Callback() {
|
||||||
|
@Override
|
||||||
|
public void federatedStatus(Status status) {
|
||||||
|
Intent intent = new Intent(context, ContextActivity.class);
|
||||||
|
intent.putExtra(Helper.ARG_STATUS, status);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void federatedAccount(Account account) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {//It's an account
|
||||||
|
CrossActionHelper.fetchRemoteAccount(context, currentAccount, matcherLink.group(2) + "@" + matcherLink.group(1), new CrossActionHelper.Callback() {
|
||||||
|
@Override
|
||||||
|
public void federatedStatus(Status status) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void federatedAccount(Account account) {
|
||||||
|
Intent intent = new Intent(context, ProfileActivity.class);
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.putSerializable(Helper.ARG_ACCOUNT, account);
|
||||||
|
intent.putExtras(b);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Helper.openBrowser(context, finalURl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateDrawState(@NonNull TextPaint ds) {
|
||||||
|
super.updateDrawState(ds);
|
||||||
|
ds.setUnderlineText(false);
|
||||||
|
ds.setColor(linkColor);
|
||||||
|
}
|
||||||
|
}, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void interaction(Context context, Spannable content, List<Mention> mentions) {
|
private static void interaction(Context context, Spannable content, List<Mention> mentions) {
|
||||||
// --- For all patterns defined in Helper class ---
|
// --- For all patterns defined in Helper class ---
|
||||||
for (Map.Entry<Helper.PatternType, Pattern> entry : Helper.patternHashMap.entrySet()) {
|
for (Map.Entry<Helper.PatternType, Pattern> entry : Helper.patternHashMap.entrySet()) {
|
||||||
|
|
Loading…
Reference in New Issue