Open link in browser when a post/account links to itself

closes #739
This commit is contained in:
Grishka
2023-11-15 18:17:08 +03:00
parent a438f633be
commit cbe243fc9e
9 changed files with 35 additions and 16 deletions

View File

@@ -516,7 +516,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
// noinspection SetTextI18n // noinspection SetTextI18n
username.setText('@'+account.acct+(isSelf ? ('@'+AccountSessionManager.getInstance().getAccount(accountID).domain) : "")); username.setText('@'+account.acct+(isSelf ? ('@'+AccountSessionManager.getInstance().getAccount(accountID).domain) : ""));
} }
CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID); CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID, account);
if(TextUtils.isEmpty(parsedBio)){ if(TextUtils.isEmpty(parsedBio)){
bio.setVisibility(View.GONE); bio.setVisibility(View.GONE);
}else{ }else{
@@ -551,7 +551,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
fields.add(joined); fields.add(joined);
for(AccountField field:account.fields){ for(AccountField field:account.fields){
field.parsedValue=ssb=HtmlParser.parse(field.value, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID); field.parsedValue=ssb=HtmlParser.parse(field.value, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID, account);
field.valueEmojis=ssb.getSpans(0, ssb.length(), CustomEmojiSpan.class); field.valueEmojis=ssb.getSpans(0, ssb.length(), CustomEmojiSpan.class);
ssb=new SpannableStringBuilder(field.name); ssb=new SpannableStringBuilder(field.name);
HtmlParser.parseCustomEmoji(ssb, account.emojis); HtmlParser.parseCustomEmoji(ssb, account.emojis);

View File

@@ -271,7 +271,7 @@ public class SignupFragment extends ToolbarFragment{
@Override @Override
public void tail(Node node, int depth){ public void tail(Node node, int depth){
if(node instanceof Element){ if(node instanceof Element){
ssb.setSpan(new LinkSpan("", SignupFragment.this::onGoBackLinkClick, LinkSpan.Type.CUSTOM, null, null), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new LinkSpan("", SignupFragment.this::onGoBackLinkClick, LinkSpan.Type.CUSTOM, null, null, null), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.setSpan(new TypefaceSpan("sans-serif-medium"), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new TypefaceSpan("sans-serif-medium"), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
} }

View File

@@ -30,7 +30,7 @@ public class AccountViewModel{
parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis); parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis);
else else
parsedName=account.displayName; parsedName=account.displayName;
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID); parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID, account);
SpannableStringBuilder ssb=new SpannableStringBuilder(parsedName); SpannableStringBuilder ssb=new SpannableStringBuilder(parsedName);
ssb.append(parsedBio); ssb.append(parsedBio);
emojiHelper.setText(ssb); emojiHelper.setText(ssb);

View File

@@ -102,7 +102,7 @@ public class LinkCardStatusDisplayItem extends StatusDisplayItem{
} }
private void onClick(View v){ private void onClick(View v){
UiUtils.openURL(itemView.getContext(), item.parentFragment.getAccountID(), item.status.card.url); UiUtils.openURL(itemView.getContext(), item.parentFragment.getAccountID(), item.status.card.url, item.status);
} }
} }
} }

View File

@@ -135,7 +135,7 @@ public abstract class StatusDisplayItem{
} }
if(!TextUtils.isEmpty(statusForContent.content)){ if(!TextUtils.isEmpty(statusForContent.content)){
SpannableStringBuilder parsedText=HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID); SpannableStringBuilder parsedText=HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID, statusForContent);
if(filtered){ if(filtered){
HtmlParser.applyFilterHighlights(fragment.getActivity(), parsedText, status.filtered); HtmlParser.applyFilterHighlights(fragment.getActivity(), parsedText, status.filtered);
} }

View File

@@ -59,7 +59,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
public void setTranslatedText(String text){ public void setTranslatedText(String text){
Status statusForContent=status.getContentStatus(); Status statusForContent=status.getContentStatus();
translatedText=HtmlParser.parse(text, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, parentFragment.getAccountID()); translatedText=HtmlParser.parse(text, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, parentFragment.getAccountID(), statusForContent);
translationEmojiHelper.setText(translatedText); translationEmojiHelper.setText(translatedText);
} }

View File

@@ -68,7 +68,7 @@ public class HtmlParser{
* @param emojis Custom emojis that are present in source as <code>:code:</code> * @param emojis Custom emojis that are present in source as <code>:code:</code>
* @return a spanned string * @return a spanned string
*/ */
public static SpannableStringBuilder parse(String source, List<Emoji> emojis, List<Mention> mentions, List<Hashtag> tags, String accountID){ public static SpannableStringBuilder parse(String source, List<Emoji> emojis, List<Mention> mentions, List<Hashtag> tags, String accountID, Object parentObject){
class SpanInfo{ class SpanInfo{
public Object span; public Object span;
public int start; public int start;
@@ -120,7 +120,7 @@ public class HtmlParser{
}else{ }else{
linkType=LinkSpan.Type.URL; linkType=LinkSpan.Type.URL;
} }
openSpans.add(new SpanInfo(new LinkSpan(href, null, linkType, accountID, linkObject), ssb.length(), el)); openSpans.add(new SpanInfo(new LinkSpan(href, null, linkType, accountID, linkObject, parentObject), ssb.length(), el));
} }
case "br" -> ssb.append('\n'); case "br" -> ssb.append('\n');
case "span" -> { case "span" -> {
@@ -216,7 +216,7 @@ public class HtmlParser{
String url=matcher.group(3); String url=matcher.group(3);
if(TextUtils.isEmpty(matcher.group(4))) if(TextUtils.isEmpty(matcher.group(4)))
url="http://"+url; url="http://"+url;
ssb.setSpan(new LinkSpan(url, null, LinkSpan.Type.URL, null, null), matcher.start(3), matcher.end(3), 0); ssb.setSpan(new LinkSpan(url, null, LinkSpan.Type.URL, null, null, null), matcher.start(3), matcher.end(3), 0);
}while(matcher.find()); // Find more URLs }while(matcher.find()); // Find more URLs
return ssb; return ssb;
} }

View File

@@ -15,13 +15,15 @@ public class LinkSpan extends CharacterStyle {
private Type type; private Type type;
private String accountID; private String accountID;
private Object linkObject; private Object linkObject;
private Object parentObject;
public LinkSpan(String link, OnLinkClickListener listener, Type type, String accountID, Object linkObject){ public LinkSpan(String link, OnLinkClickListener listener, Type type, String accountID, Object linkObject, Object parentObject){
this.listener=listener; this.listener=listener;
this.link=link; this.link=link;
this.type=type; this.type=type;
this.accountID=accountID; this.accountID=accountID;
this.linkObject=linkObject; this.linkObject=linkObject;
this.parentObject=parentObject;
} }
public int getColor(){ public int getColor(){
@@ -36,7 +38,7 @@ public class LinkSpan extends CharacterStyle {
public void onClick(Context context){ public void onClick(Context context){
switch(getType()){ switch(getType()){
case URL -> UiUtils.openURL(context, accountID, link); case URL -> UiUtils.openURL(context, accountID, link, parentObject);
case MENTION -> UiUtils.openProfileByID(context, accountID, link); case MENTION -> UiUtils.openProfileByID(context, accountID, link);
case HASHTAG -> { case HASHTAG -> {
if(linkObject instanceof Hashtag ht) if(linkObject instanceof Hashtag ht)

View File

@@ -83,6 +83,7 @@ import java.time.format.FormatStyle;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.function.BiPredicate; import java.util.function.BiPredicate;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -616,9 +617,15 @@ public class UiUtils{
return GlobalUserPreferences.theme==GlobalUserPreferences.ThemePreference.DARK; return GlobalUserPreferences.theme==GlobalUserPreferences.ThemePreference.DARK;
} }
public static void openURL(Context context, String accountID, String url){ public static void openURL(Context context, String accountID, String url, Object parentObject){
String objectURL=null;
if(parentObject instanceof Status s){
objectURL=s.url;
}else if(parentObject instanceof Account a){
objectURL=a.url;
}
Uri uri=Uri.parse(url); Uri uri=Uri.parse(url);
if(accountID!=null && "https".equals(uri.getScheme())){ if(accountID!=null && "https".equals(uri.getScheme()) && !Objects.equals(url, objectURL)){
List<String> path=uri.getPathSegments(); List<String> path=uri.getPathSegments();
if(AccountSessionManager.getInstance().getAccount(accountID).domain.equalsIgnoreCase(uri.getAuthority()) && path.size()==2 && path.get(0).matches("^@[a-zA-Z0-9_]+$") && path.get(1).matches("^[0-9]+$")){ if(AccountSessionManager.getInstance().getAccount(accountID).domain.equalsIgnoreCase(uri.getAuthority()) && path.size()==2 && path.get(0).matches("^@[a-zA-Z0-9_]+$") && path.get(1).matches("^[0-9]+$")){
// Match URLs like https://mastodon.social/@Gargron/108132679274083591 // Match URLs like https://mastodon.social/@Gargron/108132679274083591
@@ -649,10 +656,20 @@ public class UiUtils{
Bundle args=new Bundle(); Bundle args=new Bundle();
args.putString("account", accountID); args.putString("account", accountID);
if(result.statuses!=null && !result.statuses.isEmpty()){ if(result.statuses!=null && !result.statuses.isEmpty()){
args.putParcelable("status", Parcels.wrap(result.statuses.get(0))); Status s=result.statuses.get(0);
if(parentObject instanceof Status status && s.id.equals(status.id)){
launchWebBrowser(context, url);
return;
}
args.putParcelable("status", Parcels.wrap(s));
Nav.go((Activity)context, ThreadFragment.class, args); Nav.go((Activity)context, ThreadFragment.class, args);
}else if(result.accounts!=null && !result.accounts.isEmpty()){ }else if(result.accounts!=null && !result.accounts.isEmpty()){
args.putParcelable("profileAccount", Parcels.wrap(result.accounts.get(0))); Account a=result.accounts.get(0);
if(parentObject instanceof Account account && a.id.equals(account.id)){
launchWebBrowser(context, url);
return;
}
args.putParcelable("profileAccount", Parcels.wrap(a));
Nav.go((Activity)context, ProfileFragment.class, args); Nav.go((Activity)context, ProfileFragment.class, args);
}else{ }else{
launchWebBrowser(context, url); launchWebBrowser(context, url);