mirror of
https://codeberg.org/tom79/Fedilab.git
synced 2025-07-08 04:40:28 +03:00
Improve interactions
This commit is contained in:
parent
12fae6059a
commit
112701dd86
6 changed files with 297 additions and 742 deletions
|
@ -115,7 +115,7 @@ public class Account implements Serializable {
|
||||||
if (display_name == null || display_name.isEmpty()) {
|
if (display_name == null || display_name.isEmpty()) {
|
||||||
display_name = username;
|
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<View> viewWeakReference) {
|
public synchronized Spannable getSpanDisplayName(Activity activity, WeakReference<View> viewWeakReference) {
|
||||||
|
@ -126,11 +126,11 @@ public class Account implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized Spannable getSpanDisplayNameTitle(Context context, WeakReference<View> viewWeakReference, String title) {
|
public synchronized Spannable getSpanDisplayNameTitle(Context context, WeakReference<View> 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<View> viewWeakReference) {
|
public synchronized Spannable getSpanNote(Context context, WeakReference<View> 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 {
|
public static class AccountParams implements Serializable {
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class Announcement {
|
||||||
|
|
||||||
|
|
||||||
public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference) {
|
public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference) {
|
||||||
return SpannableHelper.convert(context, content, null, null, this, true, false, viewWeakReference);
|
return SpannableHelper.convert(context, content, null, null, this, viewWeakReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class Field implements Serializable {
|
||||||
if (verified_at != null && value != null) {
|
if (verified_at != null && value != null) {
|
||||||
value_span = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.verified_text));
|
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) {
|
if (value_span != null && spannable != null) {
|
||||||
spannable.setSpan(value_span, 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
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<View> viewWeakReference) {
|
public synchronized Spannable getLabelSpan(Context context, Account account, WeakReference<View> 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) {
|
if (name_span != null && spannable != null) {
|
||||||
spannable.setSpan(name_span, 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
spannable.setSpan(name_span, 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class Poll implements Serializable {
|
||||||
public transient Spannable span_title;
|
public transient Spannable span_title;
|
||||||
|
|
||||||
public Spannable getSpanTitle(Context context, Status status, WeakReference<View> viewWeakReference) {
|
public Spannable getSpanTitle(Context context, Status status, WeakReference<View> 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;
|
return span_title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,21 +134,21 @@ public class Status implements Serializable, Cloneable {
|
||||||
|
|
||||||
public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference, Callback callback) {
|
public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference, Callback callback) {
|
||||||
if (contentSpan == null) {
|
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;
|
return contentSpan;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized Spannable getSpanSpoiler(Context context, WeakReference<View> viewWeakReference, Callback callback) {
|
public synchronized Spannable getSpanSpoiler(Context context, WeakReference<View> viewWeakReference, Callback callback) {
|
||||||
if (contentSpoilerSpan == null) {
|
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;
|
return contentSpoilerSpan;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized Spannable getSpanTranslate(Context context, WeakReference<View> viewWeakReference, Callback callback) {
|
public synchronized Spannable getSpanTranslate(Context context, WeakReference<View> viewWeakReference, Callback callback) {
|
||||||
if (contentTranslateSpan == null) {
|
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;
|
return contentTranslateSpan;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,12 +52,6 @@ import androidx.lifecycle.ViewModelStoreOwner;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
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.io.IOException;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
@ -96,18 +90,16 @@ public class SpannableHelper {
|
||||||
public static final String CLICKABLE_SPAN = "CLICKABLE_SPAN";
|
public static final String CLICKABLE_SPAN = "CLICKABLE_SPAN";
|
||||||
|
|
||||||
public static Spannable convert(Context context, String text,
|
public static Spannable convert(Context context, String text,
|
||||||
Status status, Account account, Announcement announcement,
|
Status status, Account account, Announcement announcement, WeakReference<View> viewWeakReference) {
|
||||||
boolean convertHtml, boolean forceMentions, WeakReference<View> viewWeakReference) {
|
return convert(context, text, status, account, announcement, viewWeakReference, null);
|
||||||
return convert(context, text, status, account, announcement, convertHtml, forceMentions, viewWeakReference, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static int linkColor;
|
private static int linkColor;
|
||||||
|
|
||||||
|
|
||||||
public static Spannable convert(Context context, String text,
|
public static Spannable convert(Context context, String text,
|
||||||
Status status, Account account, Announcement announcement,
|
Status status, Account account, Announcement announcement,
|
||||||
boolean convertHtml,
|
|
||||||
boolean forceMentions,
|
|
||||||
WeakReference<View> viewWeakReference, Status.Callback callback) {
|
WeakReference<View> viewWeakReference, Status.Callback callback) {
|
||||||
|
|
||||||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
@ -131,24 +123,125 @@ public class SpannableHelper {
|
||||||
if (linkColor == 0) {
|
if (linkColor == 0) {
|
||||||
linkColor = -1;
|
linkColor = -1;
|
||||||
}
|
}
|
||||||
SpannableString initialContent;
|
List<Mention> mentions = new ArrayList<>();
|
||||||
if (text == null) {
|
if (status != null) {
|
||||||
return null;
|
mentions.addAll(status.mentions);
|
||||||
|
}
|
||||||
|
text = text.replaceAll("((<\\s?p\\s?>|<\\s?br\\s?/?>)>(((?!([<])).)*))", "$2<blockquote>$3</blockquote>");
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
Document htmlContent = Jsoup.parse(text);
|
|
||||||
Elements mentionElements = htmlContent.select("a.mention");
|
|
||||||
//We keep a reference to mentions
|
|
||||||
HashMap<String, String> 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<blockquote>$3</blockquote>");
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
Pattern imgPattern = Pattern.compile("<img [^>]*src=\"([^\"]+)\"[^>]*>");
|
Pattern imgPattern = Pattern.compile("<img [^>]*src=\"([^\"]+)\"[^>]*>");
|
||||||
Matcher matcherImg = imgPattern.matcher(text);
|
Matcher matcherImg = imgPattern.matcher(text);
|
||||||
HashMap<String, String> imagesToReplace = new LinkedHashMap<>();
|
HashMap<String, String> imagesToReplace = new LinkedHashMap<>();
|
||||||
|
@ -160,57 +253,15 @@ public class SpannableHelper {
|
||||||
text = text.replaceAll(Pattern.quote(matcherImg.group()), replacement);
|
text = text.replaceAll(Pattern.quote(matcherImg.group()), replacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpannableStringBuilder content;
|
|
||||||
View view = viewWeakReference.get();
|
View view = viewWeakReference.get();
|
||||||
List<Mention> mentionList = null;
|
|
||||||
List<Emoji> emojiList = null;
|
List<Emoji> emojiList = null;
|
||||||
if (status != null) {
|
if (status != null) {
|
||||||
mentionList = status.mentions;
|
|
||||||
emojiList = status.emojis;
|
emojiList = status.emojis;
|
||||||
} else if (account != null) {
|
} else if (account != null) {
|
||||||
emojiList = account.emojis;
|
emojiList = account.emojis;
|
||||||
} else if (announcement != null) {
|
} else if (announcement != null) {
|
||||||
emojiList = announcement.emojis;
|
emojiList = announcement.emojis;
|
||||||
}
|
}
|
||||||
//UrlDetails will contain links having a text different from the url
|
|
||||||
HashMap<String, String> 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("<span")) {
|
|
||||||
urlDetails.put(url, urlText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text = text.trim().replaceAll("\\s{3}", " ");
|
|
||||||
text = text.trim().replaceAll("\\s{2}", " ");
|
|
||||||
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));
|
|
||||||
|
|
||||||
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);
|
boolean animate = !sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false);
|
||||||
CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view));
|
CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view));
|
||||||
content = customEmoji.makeEmoji(content, emojiList, animate, callback);
|
content = customEmoji.makeEmoji(content, emojiList, animate, callback);
|
||||||
|
@ -234,307 +285,23 @@ public class SpannableHelper {
|
||||||
return trimSpannable(new SpannableStringBuilder(content));
|
return trimSpannable(new SpannableStringBuilder(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void linkify(Context context, SpannableStringBuilder content, HashMap<String, String> 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;
|
||||||
|
|
||||||
while (matcherLink.find()) {
|
|
||||||
int matchStart = matcherLink.start() - offSetTruncate;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
String newURL = Helper.transformURL(context, url);
|
||||||
//If URL has been transformed
|
//If URL has been transformed
|
||||||
if (newURL.compareTo(url) != 0) {
|
if (newURL.compareTo(url) != 0) {
|
||||||
content.replace(matchStart, matchEnd, newURL);
|
content.replace(start, end, newURL);
|
||||||
offSetTruncate -= (newURL.length() - url.length());
|
end = start + newURL.length();
|
||||||
matchEnd = matchStart + newURL.length();
|
url = newURL;
|
||||||
}
|
}
|
||||||
|
if (url.length() > 30 && (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("gimini://"))) {
|
||||||
//Truncate URL if needed
|
newUrl = url.substring(0, 30);
|
||||||
//TODO: add an option to disable truncated URLs
|
newUrl += "…";
|
||||||
String urlText = newURL;
|
content.replace(start, end, 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());
|
|
||||||
}
|
}
|
||||||
|
int matchEnd = start + newUrl.length();
|
||||||
|
String finalUrl = url;
|
||||||
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);
|
|
||||||
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 = 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}).start();
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
alertDialog.dismiss();
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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<String, String> urlDetails) {
|
|
||||||
|
|
||||||
for (Map.Entry<String, String> 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() {
|
content.setSpan(new LongClickableSpan() {
|
||||||
@Override
|
@Override
|
||||||
public void onLongClick(View view) {
|
public void onLongClick(View view) {
|
||||||
|
@ -544,14 +311,9 @@ public class SpannableHelper {
|
||||||
dialogBuilder.setView(popupLinksBinding.getRoot());
|
dialogBuilder.setView(popupLinksBinding.getRoot());
|
||||||
AlertDialog alertDialog = dialogBuilder.create();
|
AlertDialog alertDialog = dialogBuilder.create();
|
||||||
alertDialog.show();
|
alertDialog.show();
|
||||||
String finalURl = url;
|
|
||||||
if (urlDetails.containsValue(url)) {
|
|
||||||
finalURl = Helper.getKeyByValue(urlDetails, url);
|
|
||||||
}
|
|
||||||
String finalURl1 = finalURl;
|
|
||||||
popupLinksBinding.displayFullLink.setOnClickListener(v -> {
|
popupLinksBinding.displayFullLink.setOnClickListener(v -> {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
|
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
|
||||||
builder.setMessage(finalURl1);
|
builder.setMessage(finalUrl);
|
||||||
builder.setTitle(context.getString(R.string.display_full_link));
|
builder.setTitle(context.getString(R.string.display_full_link));
|
||||||
builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss())
|
builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss())
|
||||||
.show();
|
.show();
|
||||||
|
@ -560,7 +322,7 @@ public class SpannableHelper {
|
||||||
popupLinksBinding.shareLink.setOnClickListener(v -> {
|
popupLinksBinding.shareLink.setOnClickListener(v -> {
|
||||||
Intent sendIntent = new Intent(Intent.ACTION_SEND);
|
Intent sendIntent = new Intent(Intent.ACTION_SEND);
|
||||||
sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
|
sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
|
||||||
sendIntent.putExtra(Intent.EXTRA_TEXT, finalURl1);
|
sendIntent.putExtra(Intent.EXTRA_TEXT, finalUrl);
|
||||||
sendIntent.setType("text/plain");
|
sendIntent.setType("text/plain");
|
||||||
sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
Intent intentChooser = Intent.createChooser(sendIntent, context.getString(R.string.share_with));
|
Intent intentChooser = Intent.createChooser(sendIntent, context.getString(R.string.share_with));
|
||||||
|
@ -571,7 +333,7 @@ public class SpannableHelper {
|
||||||
|
|
||||||
popupLinksBinding.openOtherApp.setOnClickListener(v -> {
|
popupLinksBinding.openOtherApp.setOnClickListener(v -> {
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse(finalURl1));
|
intent.setData(Uri.parse(finalUrl));
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
try {
|
try {
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
|
@ -583,7 +345,7 @@ public class SpannableHelper {
|
||||||
|
|
||||||
popupLinksBinding.copyLink.setOnClickListener(v -> {
|
popupLinksBinding.copyLink.setOnClickListener(v -> {
|
||||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, finalURl1);
|
ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, finalUrl);
|
||||||
if (clipboard != null) {
|
if (clipboard != null) {
|
||||||
clipboard.setPrimaryClip(clip);
|
clipboard.setPrimaryClip(clip);
|
||||||
Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show();
|
Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show();
|
||||||
|
@ -594,7 +356,7 @@ public class SpannableHelper {
|
||||||
popupLinksBinding.checkRedirect.setOnClickListener(v -> {
|
popupLinksBinding.checkRedirect.setOnClickListener(v -> {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
URL finalUrlCheck = new URL(finalURl1);
|
URL finalUrlCheck = new URL(finalUrl);
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
String redirect = null;
|
String redirect = null;
|
||||||
|
@ -616,7 +378,7 @@ public class SpannableHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
httpsURLConnection.getInputStream().close();
|
httpsURLConnection.getInputStream().close();
|
||||||
if (redirect != null && finalURl1 != null && redirect.compareTo(finalURl1) != 0) {
|
if (redirect != null && redirect.compareTo(finalUrl) != 0) {
|
||||||
URL redirectURL = new URL(redirect);
|
URL redirectURL = new URL(redirect);
|
||||||
String host = redirectURL.getHost();
|
String host = redirectURL.getHost();
|
||||||
String protocol = redirectURL.getProtocol();
|
String protocol = redirectURL.getProtocol();
|
||||||
|
@ -629,7 +391,7 @@ public class SpannableHelper {
|
||||||
Runnable myRunnable = () -> {
|
Runnable myRunnable = () -> {
|
||||||
AlertDialog.Builder builder1 = new AlertDialog.Builder(view.getContext());
|
AlertDialog.Builder builder1 = new AlertDialog.Builder(view.getContext());
|
||||||
if (finalRedirect != null) {
|
if (finalRedirect != null) {
|
||||||
builder1.setMessage(context.getString(R.string.redirect_detected, finalURl1, finalRedirect));
|
builder1.setMessage(context.getString(R.string.redirect_detected, finalUrl, finalRedirect));
|
||||||
builder1.setNegativeButton(R.string.copy_link, (dialog, which) -> {
|
builder1.setNegativeButton(R.string.copy_link, (dialog, which) -> {
|
||||||
ClipboardManager clipboard1 = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
ClipboardManager clipboard1 = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
ClipData clip1 = ClipData.newPlainText(Helper.CLIP_BOARD, finalRedirect);
|
ClipData clip1 = ClipData.newPlainText(Helper.CLIP_BOARD, finalRedirect);
|
||||||
|
@ -642,7 +404,7 @@ public class SpannableHelper {
|
||||||
builder1.setNeutralButton(R.string.share_link, (dialog, which) -> {
|
builder1.setNeutralButton(R.string.share_link, (dialog, which) -> {
|
||||||
Intent sendIntent1 = new Intent(Intent.ACTION_SEND);
|
Intent sendIntent1 = new Intent(Intent.ACTION_SEND);
|
||||||
sendIntent1.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
|
sendIntent1.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
|
||||||
sendIntent1.putExtra(Intent.EXTRA_TEXT, finalURl1);
|
sendIntent1.putExtra(Intent.EXTRA_TEXT, finalUrl);
|
||||||
sendIntent1.setType("text/plain");
|
sendIntent1.setType("text/plain");
|
||||||
context.startActivity(Intent.createChooser(sendIntent1, context.getString(R.string.share_with)));
|
context.startActivity(Intent.createChooser(sendIntent1, context.getString(R.string.share_with)));
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
|
@ -672,20 +434,13 @@ public class SpannableHelper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(@NonNull View textView) {
|
public void onClick(@NonNull View textView) {
|
||||||
String finalURl = url;
|
|
||||||
if (urlDetails.containsValue(url)) {
|
|
||||||
finalURl = Helper.getKeyByValue(urlDetails, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
textView.setTag(CLICKABLE_SPAN);
|
textView.setTag(CLICKABLE_SPAN);
|
||||||
Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$");
|
Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$");
|
||||||
Matcher matcherLink = null;
|
Matcher matcherLink = link.matcher(content);
|
||||||
if (finalURl != null) {
|
if (matcherLink.find() && !finalUrl.contains("medium.com")) {
|
||||||
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
|
if (matcherLink.group(3) != null && Objects.requireNonNull(matcherLink.group(3)).length() > 0) { //It's a toot
|
||||||
CrossActionHelper.fetchRemoteStatus(context, currentAccount, finalURl, new CrossActionHelper.Callback() {
|
CrossActionHelper.fetchRemoteStatus(context, currentAccount, finalUrl, new CrossActionHelper.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void federatedStatus(Status status) {
|
public void federatedStatus(Status status) {
|
||||||
Intent intent = new Intent(context, ContextActivity.class);
|
Intent intent = new Intent(context, ContextActivity.class);
|
||||||
|
@ -716,9 +471,8 @@ public class SpannableHelper {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Helper.openBrowser(context, finalURl);
|
Helper.openBrowser(context, finalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -729,46 +483,9 @@ public class SpannableHelper {
|
||||||
ds.setColor(linkColor);
|
ds.setColor(linkColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
}, start, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void emails(Context context, Spannable content) {
|
private static void emails(Context context, Spannable content) {
|
||||||
// --- For all patterns defined in Helper class ---
|
// --- For all patterns defined in Helper class ---
|
||||||
|
@ -809,167 +526,6 @@ public class SpannableHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void interaction(Context context, Spannable content, Status status, List<Mention> mentions, boolean forceMentions, HashMap<String, String> mentionsMap) {
|
|
||||||
// --- For all patterns defined in Helper class ---
|
|
||||||
for (Map.Entry<Helper.PatternType, Pattern> 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<String, Integer> 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) {
|
public static void addTagToFilter(Context context, String tag, Status status, Filter filter) {
|
||||||
for (Filter.KeywordsAttributes keywords : filter.keywords) {
|
for (Filter.KeywordsAttributes keywords : filter.keywords) {
|
||||||
if (keywords.keyword.equalsIgnoreCase(tag)) {
|
if (keywords.keyword.equalsIgnoreCase(tag)) {
|
||||||
|
@ -1006,7 +562,6 @@ public class SpannableHelper {
|
||||||
*
|
*
|
||||||
* @param status {@link Status} - Status concerned by the spannable transformation
|
* @param status {@link Status} - Status concerned by the spannable transformation
|
||||||
* @param content String - text to convert, it can be content, spoiler, poll items, etc.
|
* @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) {
|
private static void convertOuich(@NonNull Status status, SpannableStringBuilder content) {
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue