From 112701dd866a68694f376b0dd7f6f50c2cb8594a Mon Sep 17 00:00:00 2001 From: Thomas Date: Sat, 31 Dec 2022 14:59:23 +0100 Subject: [PATCH] Improve interactions --- .../android/client/entities/api/Account.java | 6 +- .../client/entities/api/Announcement.java | 2 +- .../android/client/entities/api/Field.java | 4 +- .../android/client/entities/api/Poll.java | 2 +- .../android/client/entities/api/Status.java | 6 +- .../android/helper/SpannableHelper.java | 1019 +++++------------ 6 files changed, 297 insertions(+), 742 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java index 9c423554..6631be8f 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java @@ -115,7 +115,7 @@ public class Account implements Serializable { if (display_name == null || display_name.isEmpty()) { display_name = username; } - return SpannableHelper.convert(context, display_name, null, this, null, false, false, viewWeakReference); + return SpannableHelper.convert(context, display_name, null, this, null, viewWeakReference); } public synchronized Spannable getSpanDisplayName(Activity activity, WeakReference viewWeakReference) { @@ -126,11 +126,11 @@ public class Account implements Serializable { } public synchronized Spannable getSpanDisplayNameTitle(Context context, WeakReference viewWeakReference, String title) { - return SpannableHelper.convert(context, title, null, this, null, false, false, viewWeakReference); + return SpannableHelper.convert(context, title, null, this, null, viewWeakReference); } public synchronized Spannable getSpanNote(Context context, WeakReference viewWeakReference) { - return SpannableHelper.convert(context, note, null, this, null, true, true, viewWeakReference); + return SpannableHelper.convert(context, note, null, this, null, viewWeakReference); } public static class AccountParams implements Serializable { diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java b/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java index 808ac6e5..8bf18863 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java @@ -56,7 +56,7 @@ public class Announcement { public synchronized Spannable getSpanContent(Context context, WeakReference viewWeakReference) { - return SpannableHelper.convert(context, content, null, null, this, true, false, viewWeakReference); + return SpannableHelper.convert(context, content, null, null, this, viewWeakReference); } } diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Field.java b/app/src/main/java/app/fedilab/android/client/entities/api/Field.java index af574ee3..f038740e 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Field.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Field.java @@ -47,7 +47,7 @@ public class Field implements Serializable { if (verified_at != null && value != null) { value_span = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.verified_text)); } - Spannable spannable = SpannableHelper.convert(context, value, null, account, null, true, true, viewWeakReference); + Spannable spannable = SpannableHelper.convert(context, value, null, account, null, viewWeakReference); if (value_span != null && spannable != null) { spannable.setSpan(value_span, 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } @@ -57,7 +57,7 @@ public class Field implements Serializable { public synchronized Spannable getLabelSpan(Context context, Account account, WeakReference viewWeakReference) { - Spannable spannable = SpannableHelper.convert(context, name, null, account, null, true, true, viewWeakReference); + Spannable spannable = SpannableHelper.convert(context, name, null, account, null, viewWeakReference); if (name_span != null && spannable != null) { spannable.setSpan(name_span, 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java b/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java index cf0d1d8b..98b161a9 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java @@ -61,7 +61,7 @@ public class Poll implements Serializable { public transient Spannable span_title; public Spannable getSpanTitle(Context context, Status status, WeakReference viewWeakReference) { - span_title = SpannableHelper.convert(context, title, status, null, null, false, false, viewWeakReference); + span_title = SpannableHelper.convert(context, title, status, null, null, viewWeakReference); return span_title; } } diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Status.java b/app/src/main/java/app/fedilab/android/client/entities/api/Status.java index e6a03074..ecc2a07e 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Status.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Status.java @@ -134,21 +134,21 @@ public class Status implements Serializable, Cloneable { public synchronized Spannable getSpanContent(Context context, WeakReference viewWeakReference, Callback callback) { if (contentSpan == null) { - contentSpan = SpannableHelper.convert(context, content, this, null, null, true, false, viewWeakReference, callback); + contentSpan = SpannableHelper.convert(context, content, this, null, null, viewWeakReference, callback); } return contentSpan; } public synchronized Spannable getSpanSpoiler(Context context, WeakReference viewWeakReference, Callback callback) { if (contentSpoilerSpan == null) { - contentSpoilerSpan = SpannableHelper.convert(context, spoiler_text, this, null, null, true, false, viewWeakReference, callback); + contentSpoilerSpan = SpannableHelper.convert(context, spoiler_text, this, null, null, viewWeakReference, callback); } return contentSpoilerSpan; } public synchronized Spannable getSpanTranslate(Context context, WeakReference viewWeakReference, Callback callback) { if (contentTranslateSpan == null) { - contentTranslateSpan = SpannableHelper.convert(context, translationContent, this, null, null, true, false, viewWeakReference, callback); + contentTranslateSpan = SpannableHelper.convert(context, translationContent, this, null, null, viewWeakReference, callback); } return contentTranslateSpan; } diff --git a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java index 26cb125d..4dc19444 100644 --- a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java @@ -52,12 +52,6 @@ import androidx.lifecycle.ViewModelStoreOwner; import androidx.preference.PreferenceManager; import com.bumptech.glide.Glide; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; import java.io.IOException; import java.lang.ref.WeakReference; @@ -96,18 +90,16 @@ public class SpannableHelper { public static final String CLICKABLE_SPAN = "CLICKABLE_SPAN"; public static Spannable convert(Context context, String text, - Status status, Account account, Announcement announcement, - boolean convertHtml, boolean forceMentions, WeakReference viewWeakReference) { - return convert(context, text, status, account, announcement, convertHtml, forceMentions, viewWeakReference, null); + Status status, Account account, Announcement announcement, WeakReference viewWeakReference) { + return convert(context, text, status, account, announcement, viewWeakReference, null); } private static int linkColor; + public static Spannable convert(Context context, String text, Status status, Account account, Announcement announcement, - boolean convertHtml, - boolean forceMentions, WeakReference viewWeakReference, Status.Callback callback) { SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); @@ -131,24 +123,125 @@ public class SpannableHelper { if (linkColor == 0) { linkColor = -1; } - SpannableString initialContent; - if (text == null) { - return null; + List mentions = new ArrayList<>(); + if (status != null) { + mentions.addAll(status.mentions); } - Document htmlContent = Jsoup.parse(text); - Elements mentionElements = htmlContent.select("a.mention"); - //We keep a reference to mentions - HashMap mentionsMap = new HashMap<>(); - if (mentionElements.size() > 0) { - for (int i = 0; i < mentionElements.size(); i++) { - Element mentionElement = mentionElements.get(i); - String href = mentionElement.attr("href"); - String mention = mentionElement.text(); - mentionsMap.put(mention, href); + text = text.replaceAll("((<\\s?p\\s?>|<\\s?br\\s?/?>)>(((?!([<])).)*))", "$2
$3
"); + text = text.trim().replaceAll("\\s{3}", "   "); + text = text.trim().replaceAll("\\s{2}", "  "); + SpannableString initialContent; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + initialContent = new SpannableString(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)); + else + initialContent = new SpannableString(Html.fromHtml(text)); + + //Get all links + SpannableStringBuilder content = new SpannableStringBuilder(initialContent); + URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class); + //Loop through links + for (URLSpan span : urls) { + String url = span.getURL(); + + + int start = content.getSpanStart(span); + int end = content.getSpanEnd(span); + content.removeSpan(span); + //Get the matching word associated to the URL + String word = content.subSequence(start, end).toString(); + if (word.startsWith("@") || word.startsWith("#")) { + content.setSpan(new LongClickableSpan() { + @Override + public void onLongClick(View textView) { + textView.setTag(CLICKABLE_SPAN); + if (word.startsWith("#") && BaseMainActivity.filterFetched && MainActivity.mainFilters != null) { + String tag = word.trim(); + if (!tag.startsWith("#")) { + tag = "#" + tag; + } + Filter fedilabFilter = null; + for (Filter filter : MainActivity.mainFilters) { + if (filter.title.equals(Helper.FEDILAB_MUTED_HASHTAGS)) { + fedilabFilter = filter; + break; + } + } + //Filter for Fedilab doesn't exist we have to create it + if (fedilabFilter == null) { + Filter.FilterParams filterParams = new Filter.FilterParams(); + filterParams.title = Helper.FEDILAB_MUTED_HASHTAGS; + filterParams.filter_action = "hide"; + filterParams.context = new ArrayList<>(); + filterParams.context.add("home"); + filterParams.context.add("public"); + filterParams.context.add("thread"); + filterParams.context.add("account"); + String finalTag = tag; + FiltersVM filtersVM = new ViewModelProvider((ViewModelStoreOwner) context).get(FiltersVM.class); + filtersVM.addFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filterParams) + .observe((LifecycleOwner) context, filter -> { + if (filter != null) { + MainActivity.mainFilters.add(filter); + addTagToFilter(context, finalTag, status, filter); + } + }); + } else { + addTagToFilter(context, tag, status, fedilabFilter); + } + } + } + + @Override + public void onClick(@NonNull View textView) { + textView.setTag(CLICKABLE_SPAN); + Intent intent; + Bundle b; + if (word.startsWith("#")) { + intent = new Intent(context, HashTagActivity.class); + b = new Bundle(); + b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim()); + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } else if (word.startsWith("@")) { + intent = new Intent(context, ProfileActivity.class); + b = new Bundle(); + Mention targetedMention = null; + + for (Mention mention : mentions) { + if (word.compareToIgnoreCase("@" + mention.username) == 0) { + targetedMention = mention; + break; + } + } + if (targetedMention != null) { + b.putString(Helper.ARG_USER_ID, targetedMention.id); + } else { + b.putString(Helper.ARG_MENTION, word); + } + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + if (linkColor != -1) { + ds.setColor(linkColor); + } + } + + }, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } else { + makeLinks(context, content, url, start, end); } + replaceQuoteSpans(context, content); + emails(context, content); } - text = text.replaceAll("((<\\s?p\\s?>|<\\s?br\\s?\\/?>)>(((?!([<])).)*))", "$2
$3
"); Pattern imgPattern = Pattern.compile("]*src=\"([^\"]+)\"[^>]*>"); Matcher matcherImg = imgPattern.matcher(text); HashMap imagesToReplace = new LinkedHashMap<>(); @@ -160,57 +253,15 @@ public class SpannableHelper { text = text.replaceAll(Pattern.quote(matcherImg.group()), replacement); } - SpannableStringBuilder content; View view = viewWeakReference.get(); - List mentionList = null; List emojiList = null; if (status != null) { - mentionList = status.mentions; emojiList = status.emojis; } else if (account != null) { emojiList = account.emojis; } else if (announcement != null) { emojiList = announcement.emojis; } - //UrlDetails will contain links having a text different from the url - HashMap urlDetails = new HashMap<>(); - if (convertHtml) { - Matcher matcherALink = Helper.aLink.matcher(text); - - //We stock details - while (matcherALink.find()) { - String urlText = matcherALink.group(3); - String url = matcherALink.group(2); - if (urlText != null && urlText.startsWith(">")) { - urlText = urlText.substring(1); - } - if (url != null && urlText != null && !url.equalsIgnoreCase(urlText) && !urlText.contains("= Build.VERSION_CODES.N) - initialContent = new SpannableString(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)); - else - initialContent = new SpannableString(Html.fromHtml(text)); - - content = new SpannableStringBuilder(initialContent); - URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class); - for (URLSpan span : urls) { - content.removeSpan(span); - } - //Make tags, mentions, groups - interaction(context, content, status, mentionList, forceMentions, mentionsMap); - //Make all links - linkify(context, content, urlDetails); - linkifyURL(context, content, urlDetails); - emails(context, content); - gemini(context, content); - replaceQuoteSpans(context, content); - } else { - content = new SpannableStringBuilder(text); - } boolean animate = !sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view)); content = customEmoji.makeEmoji(content, emojiList, animate, callback); @@ -234,542 +285,208 @@ public class SpannableHelper { return trimSpannable(new SpannableStringBuilder(content)); } - private static void linkify(Context context, SpannableStringBuilder content, HashMap urlDetails) { - //--- URLs ---- - Matcher matcherLink = Patterns.WEB_URL.matcher(content); - int offSetTruncate = 0; + private static void makeLinks(Context context, SpannableStringBuilder content, String url, int start, int end) { + String newUrl = url; + String newURL = Helper.transformURL(context, url); + //If URL has been transformed + if (newURL.compareTo(url) != 0) { + content.replace(start, end, newURL); + end = start + newURL.length(); + url = newURL; + } + if (url.length() > 30 && (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("gimini://"))) { + newUrl = url.substring(0, 30); + newUrl += "…"; + content.replace(start, end, newUrl); + } + int matchEnd = start + newUrl.length(); + String finalUrl = url; + content.setSpan(new LongClickableSpan() { + @Override + public void onLongClick(View view) { + Context mContext = view.getContext(); + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext); + PopupLinksBinding popupLinksBinding = PopupLinksBinding.inflate(LayoutInflater.from(context)); + dialogBuilder.setView(popupLinksBinding.getRoot()); + AlertDialog alertDialog = dialogBuilder.create(); + alertDialog.show(); + popupLinksBinding.displayFullLink.setOnClickListener(v -> { + AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + builder.setMessage(finalUrl); + 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, finalUrl); + 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(finalUrl)); + 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(); + }); - while (matcherLink.find()) { - int matchStart = matcherLink.start() - offSetTruncate; - int matchEnd = matchStart + matcherLink.group().length(); - if (matchEnd > content.toString().length()) { - matchEnd = content.toString().length(); - } + popupLinksBinding.copyLink.setOnClickListener(v -> { + ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, finalUrl); + if (clipboard != null) { + clipboard.setPrimaryClip(clip); + Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show(); + } + alertDialog.dismiss(); + }); - if (content.toString().length() < matchEnd || matchStart < 0 || matchStart > matchEnd) { - continue; - } + popupLinksBinding.checkRedirect.setOnClickListener(v -> { + try { - - final String url = content.toString().substring(matchStart, matchEnd); - if (urlDetails.containsKey(url)) { - continue; - } - - ClickableSpan[] clickableSpans = content.getSpans(matchStart, matchEnd, ClickableSpan.class); - if (clickableSpans != null) { - for (ClickableSpan clickableSpan : clickableSpans) { - content.removeSpan(clickableSpan); - } - } - content.removeSpan(clickableSpans); - String newURL = Helper.transformURL(context, url); - //If URL has been transformed - if (newURL.compareTo(url) != 0) { - content.replace(matchStart, matchEnd, newURL); - offSetTruncate -= (newURL.length() - url.length()); - matchEnd = matchStart + newURL.length(); - } - - //Truncate URL if needed - //TODO: add an option to disable truncated URLs - String urlText = newURL; - if (newURL.length() > 30 && !urlDetails.containsKey(urlText) && !urlText.startsWith("gemini")) { - urlText = urlText.substring(0, 30); - urlText += "…"; - content.replace(matchStart, matchEnd, urlText); - matchEnd = matchStart + 31; - offSetTruncate += (newURL.length() - urlText.length()); - } - - - if (matchEnd <= content.length() && matchEnd >= matchStart) { - content.setSpan(new LongClickableSpan() { - @Override - public void onLongClick(View view) { - Context mContext = view.getContext(); - MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(mContext); - PopupLinksBinding popupLinksBinding = PopupLinksBinding.inflate(LayoutInflater.from(context)); - materialAlertDialogBuilder.setView(popupLinksBinding.getRoot()); - AlertDialog alertDialog = materialAlertDialogBuilder.create(); - alertDialog.show(); - String finalURl = newURL; - String uniqueUrl = newURL.endsWith("…") ? newURL : newURL + "…"; - if (urlDetails.containsValue(uniqueUrl)) { - finalURl = Helper.getKeyByValue(urlDetails, uniqueUrl); - } - if (finalURl == null) { - return; - } - if (finalURl.startsWith("http://")) { - finalURl = finalURl.replace("http://", "https://"); - } - String finalURl1 = finalURl; - popupLinksBinding.displayFullLink.setOnClickListener(v -> { - AlertDialog.Builder builder = new AlertDialog.Builder(mContext); - 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); + URL finalUrlCheck = new URL(finalUrl); + new Thread(() -> { 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> map = httpsURLConnection.getHeaderFields(); - for (Map.Entry> entry : map.entrySet()) { - if (entry.toString().toLowerCase().startsWith("location")) { - Matcher matcher = Patterns.WEB_URL.matcher(entry.toString()); - if (matcher.find()) { - redirect = matcher.group(1); - } - } + 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> map = httpsURLConnection.getHeaderFields(); + for (Map.Entry> entry : map.entrySet()) { + if (entry.toString().toLowerCase().startsWith("location")) { + Matcher matcher = Patterns.WEB_URL.matcher(entry.toString()); + if (matcher.find()) { + redirect = matcher.group(1); } } - httpsURLConnection.getInputStream().close(); - if (redirect != 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()); - 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(); } + } + httpsURLConnection.getInputStream().close(); + if (redirect != null && redirect.compareTo(finalUrl) != 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()); + if (finalRedirect != null) { + builder1.setMessage(context.getString(R.string.redirect_detected, finalUrl, 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, finalUrl); + 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(); - }).start(); - } catch (MalformedURLException e) { + }; + mainHandler.post(myRunnable); + } catch (IOException e) { e.printStackTrace(); } - alertDialog.dismiss(); - }); - + }).start(); + } catch (MalformedURLException e) { + e.printStackTrace(); } - @Override - public void onClick(@NonNull View textView) { - String finalURl = newURL; - String finalURl2 = url; - String uniqueNewURL = newURL.endsWith("…") ? newURL : newURL + "…"; - if (urlDetails.containsValue(uniqueNewURL)) { - finalURl = Helper.getKeyByValue(urlDetails, uniqueNewURL); - } - String uniqueUrl = url.endsWith("…") ? url : url + "…"; - if (urlDetails.containsValue(uniqueUrl)) { - finalURl2 = Helper.getKeyByValue(urlDetails, uniqueUrl); - } - textView.setTag(CLICKABLE_SPAN); - Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$"); - Matcher matcherLink = null; - if (finalURl2 != null) { - matcherLink = link.matcher(finalURl2); - } - if (finalURl2 != null && matcherLink.find() && !finalURl2.contains("medium.com")) { - if (matcherLink.group(3) != null && Objects.requireNonNull(matcherLink.group(3)).length() > 0) { //It's a toot - CrossActionHelper.fetchRemoteStatus(context, currentAccount, finalURl2, 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); - } + alertDialog.dismiss(); + }); - @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); - if (linkColor != -1) { - ds.setColor(linkColor); - } - } - - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } - } - } - private static void linkifyURL(Context context, SpannableStringBuilder content, HashMap urlDetails) { + @Override + public void onClick(@NonNull View textView) { - for (Map.Entry entry : urlDetails.entrySet()) { - String value = entry.getValue(); - if (value.startsWith("@") || value.startsWith("#")) { - continue; - } - 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)); - if (contentUrl.toString().trim().isEmpty()) { - continue; - } - Pattern word = Pattern.compile(Pattern.quote(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; - } - - ClickableSpan[] clickableSpans = content.getSpans(matchStart, matchEnd, ClickableSpan.class); - if (clickableSpans != null) { - for (ClickableSpan clickableSpan : clickableSpans) { - content.removeSpan(clickableSpan); - } - } - content.removeSpan(clickableSpans); - - 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); - 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); - 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)); + textView.setTag(CLICKABLE_SPAN); + Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$"); + Matcher matcherLink = link.matcher(content); + if (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); - 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> map = httpsURLConnection.getHeaderFields(); - for (Map.Entry> entry : map.entrySet()) { - if (entry.toString().toLowerCase().startsWith("location")) { - Matcher matcher = Patterns.WEB_URL.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()); - 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); + context.startActivity(intent); } - 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); + @Override + public void federatedAccount(Account account) { } - 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); + }); + } 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 updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - if (linkColor != -1) { - ds.setColor(linkColor); + @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); } - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + }); + } + } else { + Helper.openBrowser(context, finalUrl); } } - } - } - private static void gemini(Context context, Spannable content) { - // --- For all patterns defined in Helper class --- - Pattern pattern = Helper.geminiPattern; - Matcher matcher = pattern.matcher(content); - while (matcher.find()) { - int matchStart = matcher.start(); - int matchEnd = matcher.end(); - String geminiLink = content.toString().substring(matchStart, matchEnd); - if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) { - ClickableSpan[] clickableSpans = content.getSpans(matchStart, matchEnd, ClickableSpan.class); - if (clickableSpans != null) { - for (ClickableSpan clickableSpan : clickableSpans) { - content.removeSpan(clickableSpan); - } + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + if (linkColor != -1) { + ds.setColor(linkColor); } - content.removeSpan(clickableSpans); - content.setSpan(new ClickableSpan() { - @Override - public void onClick(@NonNull View textView) { - Helper.openBrowser(context, geminiLink); - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - if (linkColor != -1) { - ds.setColor(linkColor); - } - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } - } + }, start, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } + private static void emails(Context context, Spannable content) { // --- For all patterns defined in Helper class --- Pattern pattern = Helper.emailPattern; @@ -809,167 +526,6 @@ public class SpannableHelper { } } - private static void interaction(Context context, Spannable content, Status status, List mentions, boolean forceMentions, HashMap mentionsMap) { - // --- For all patterns defined in Helper class --- - for (Map.Entry entry : Helper.patternHashMap.entrySet()) { - Helper.PatternType patternType = entry.getKey(); - Pattern pattern = entry.getValue(); - Matcher matcher = pattern.matcher(content); - if (pattern == Helper.mentionPattern && mentions == null && !forceMentions) { - continue; - } else if (pattern == Helper.mentionLongPattern && mentions == null && !forceMentions) { - continue; - } - - while (matcher.find()) { - int matchStart = matcher.start(); - int matchEnd = matcher.end(); - String word = content.toString().substring(matchStart, matchEnd); - if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) { - URLSpan[] span = content.getSpans(matchStart, matchEnd, URLSpan.class); - content.removeSpan(span); - content.setSpan(new LongClickableSpan() { - @Override - public void onLongClick(View textView) { - textView.setTag(CLICKABLE_SPAN); - if (patternType == Helper.PatternType.TAG && BaseMainActivity.filterFetched && MainActivity.mainFilters != null) { - String tag = word.trim(); - if (!tag.startsWith("#")) { - tag = "#" + tag; - } - Filter fedilabFilter = null; - for (Filter filter : MainActivity.mainFilters) { - if (filter.title.equals(Helper.FEDILAB_MUTED_HASHTAGS)) { - fedilabFilter = filter; - break; - } - } - //Filter for Fedilab doesn't exist we have to create it - if (fedilabFilter == null) { - Filter.FilterParams filterParams = new Filter.FilterParams(); - filterParams.title = Helper.FEDILAB_MUTED_HASHTAGS; - filterParams.filter_action = "hide"; - filterParams.context = new ArrayList<>(); - filterParams.context.add("home"); - filterParams.context.add("public"); - filterParams.context.add("thread"); - filterParams.context.add("account"); - String finalTag = tag; - FiltersVM filtersVM = new ViewModelProvider((ViewModelStoreOwner) context).get(FiltersVM.class); - filtersVM.addFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filterParams) - .observe((LifecycleOwner) context, filter -> { - if (filter != null) { - MainActivity.mainFilters.add(filter); - addTagToFilter(context, finalTag, status, filter); - } - }); - } else { - addTagToFilter(context, tag, status, fedilabFilter); - } - } - } - - @Override - public void onClick(@NonNull View textView) { - textView.setTag(CLICKABLE_SPAN); - switch (patternType) { - case TAG: - Intent intent = new Intent(context, HashTagActivity.class); - Bundle b = new Bundle(); - b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim()); - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case GROUP: - break; - case MENTION: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - Mention targetedMention = null; - String acct = null; - HashMap countUsername = new HashMap<>(); - //Mentions is retrieved with associated Mentions array - if (mentions != null) { - for (Mention mention : mentions) { - Integer count = countUsername.get(mention.username); - if (count == null) { - count = 0; - } - if (countUsername.containsKey(mention.username)) { - countUsername.put(mention.username, count + 1); - } else { - countUsername.put(mention.username, 1); - } - } - for (Mention mention : mentions) { - Integer count = countUsername.get(mention.username); - if (count == null) { - count = 0; - } - if (word.trim().compareToIgnoreCase("@" + mention.username) == 0 && count == 1) { - targetedMention = mention; - break; - } - } - } else if (mentionsMap.containsKey(word.trim())) {//Mentions will be find through its URL - URL url; - try { - url = new URL(mentionsMap.get(word.trim())); - acct = word.trim() + "@" + url.getHost(); - } catch (MalformedURLException e) { - e.printStackTrace(); - } - } - if (targetedMention != null) { - b.putString(Helper.ARG_USER_ID, targetedMention.id); - } else { - b.putString(Helper.ARG_MENTION, acct != null ? acct : word.trim()); - } - - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case MENTION_LONG: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - targetedMention = null; - if (mentions != null) { - for (Mention mention : mentions) { - if (word.trim().substring(1).compareToIgnoreCase("@" + mention.acct) == 0) { - targetedMention = mention; - break; - } - } - } - if (targetedMention != null) { - b.putString(Helper.ARG_USER_ID, targetedMention.id); - } else { - b.putString(Helper.ARG_MENTION, word.trim()); - } - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - } - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - if (linkColor != -1) { - ds.setColor(linkColor); - } - } - - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - } - } - } - public static void addTagToFilter(Context context, String tag, Status status, Filter filter) { for (Filter.KeywordsAttributes keywords : filter.keywords) { if (keywords.keyword.equalsIgnoreCase(tag)) { @@ -1006,7 +562,6 @@ public class SpannableHelper { * * @param status {@link Status} - Status concerned by the spannable transformation * @param content String - text to convert, it can be content, spoiler, poll items, etc. - * @return Spannable string */ private static void convertOuich(@NonNull Status status, SpannableStringBuilder content) {