diff --git a/mastodon/build.gradle b/mastodon/build.gradle index df403548..c24dc3e4 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -90,7 +90,7 @@ dependencies { implementation 'me.grishka.litex:viewpager:1.0.0' implementation 'me.grishka.litex:viewpager2:1.0.0' implementation 'me.grishka.litex:palette:1.0.0' - implementation 'me.grishka.appkit:appkit:1.4.2' + implementation 'me.grishka.appkit:appkit:1.4.3' implementation 'com.google.code.gson:gson:2.8.9' implementation 'org.jsoup:jsoup:1.14.3' implementation 'com.squareup:otto:1.3.8' diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/CreateListAddMembersFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/CreateListAddMembersFragment.java index a5406726..f3e366dd 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/CreateListAddMembersFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/CreateListAddMembersFragment.java @@ -84,7 +84,7 @@ public class CreateListAddMembersFragment extends BaseAccountListFragment implem public void onSuccess(HeaderPaginationList result){ for(Account acc:result) accountIDsInList.add(acc.id); - onDataLoaded(result.stream().map(a->new AccountViewModel(a, accountID)).collect(Collectors.toList())); + onDataLoaded(result.stream().map(a->new AccountViewModel(a, accountID, getActivity())).collect(Collectors.toList())); } }) .exec(accountID); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListMembersFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListMembersFragment.java index 13f5aba9..0e14a726 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListMembersFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListMembersFragment.java @@ -190,7 +190,7 @@ public class ListMembersFragment extends PaginatedAccountListFragment implements @Subscribe public void onAccountAddedToList(AccountAddedToListEvent ev){ if(ev.accountID.equals(accountID) && ev.listID.equals(followList.id)){ - data.add(new AccountViewModel(ev.account, accountID)); + data.add(new AccountViewModel(ev.account, accountID, getActivity())); list.getAdapter().notifyItemInserted(data.size()-1); } } @@ -337,7 +337,7 @@ public class ListMembersFragment extends PaginatedAccountListFragment implements onDone.run(); for(Account acc:accounts){ accountIDsInList.add(acc.id); - data.add(new AccountViewModel(acc, accountID)); + data.add(new AccountViewModel(acc, accountID, getActivity())); } list.getAdapter().notifyItemRangeInserted(data.size()-accounts.size(), accounts.size()); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationRequestsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationRequestsFragment.java index ba46edf3..6bb59726 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationRequestsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationRequestsFragment.java @@ -80,7 +80,7 @@ public class NotificationRequestsFragment extends MastodonRecyclerFragment result){ setEmptyText(R.string.no_search_results); - onDataLoaded(result.stream().map(a->new AccountViewModel(a, accountID)).collect(Collectors.toList()), false); + onDataLoaded(result.stream().map(a->new AccountViewModel(a, accountID, getActivity())).collect(Collectors.toList()), false); } protected String getSearchViewPlaceholder(){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/AddNewListMembersFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/AddNewListMembersFragment.java index 802318b9..2de25f56 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/AddNewListMembersFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/AddNewListMembersFragment.java @@ -44,7 +44,7 @@ public class AddNewListMembersFragment extends AccountSearchFragment{ @Override public void onSuccess(HeaderPaginationList result){ setEmptyText(""); - onDataLoaded(result.stream().map(a->new AccountViewModel(a, accountID)).collect(Collectors.toList()), result.nextPageUri!=null); + onDataLoaded(result.stream().map(a->new AccountViewModel(a, accountID, getActivity())).collect(Collectors.toList()), result.nextPageUri!=null); maxID=result.getNextPageMaxID(); } }) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/PaginatedAccountListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/PaginatedAccountListFragment.java index 1bf665fb..642a0307 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/PaginatedAccountListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/PaginatedAccountListFragment.java @@ -24,7 +24,7 @@ public abstract class PaginatedAccountListFragment extends BaseAccountListFragme nextMaxID=result.nextPageUri.getQueryParameter("max_id"); else nextMaxID=null; - onDataLoaded(result.stream().map(a->new AccountViewModel(a, accountID)).collect(Collectors.toList()), nextMaxID!=null); + onDataLoaded(result.stream().map(a->new AccountViewModel(a, accountID, getActivity())).collect(Collectors.toList()), nextMaxID!=null); } }) .exec(accountID); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverAccountsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverAccountsFragment.java index 04801292..05b6bb8a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverAccountsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverAccountsFragment.java @@ -31,7 +31,7 @@ public class DiscoverAccountsFragment extends BaseAccountListFragment implements .setCallback(new SimpleCallback<>(this){ @Override public void onSuccess(List result){ - List accounts=result.stream().map(fs->new AccountViewModel(fs.account, accountID)).collect(Collectors.toList()); + List accounts=result.stream().map(fs->new AccountViewModel(fs.account, accountID, getActivity())).collect(Collectors.toList()); onDataLoaded(accounts, false); bannerHelper.onBannerBecameVisible(); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchQueryFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchQueryFragment.java index 16768a38..eada4ece 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchQueryFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchQueryFragment.java @@ -109,7 +109,7 @@ public class SearchQueryFragment extends MastodonRecyclerFragment{ - SearchResultViewModel vm=new SearchResultViewModel(sr, accountID, true); + SearchResultViewModel vm=new SearchResultViewModel(sr, accountID, true, getActivity()); if(sr.type==SearchResult.Type.HASHTAG){ vm.hashtagItem.setOnClick(i->openHashtag(sr)); } @@ -126,7 +126,7 @@ public class SearchQueryFragment extends MastodonRecyclerFragment{ - SearchResultViewModel vm=new SearchResultViewModel(sr, accountID, false); + SearchResultViewModel vm=new SearchResultViewModel(sr, accountID, false, getActivity()); if(sr.type==SearchResult.Type.HASHTAG){ vm.hashtagItem.setOnClick(i->openHashtag(sr)); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingFollowSuggestionsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingFollowSuggestionsFragment.java index dc9f946e..13ccd5a7 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingFollowSuggestionsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingFollowSuggestionsFragment.java @@ -70,7 +70,7 @@ public class OnboardingFollowSuggestionsFragment extends BaseAccountListFragment .setCallback(new SimpleCallback<>(this){ @Override public void onSuccess(List result){ - onDataLoaded(result.stream().map(fs->new AccountViewModel(fs.account, accountID).stripLinksFromBio()).collect(Collectors.toList()), false); + onDataLoaded(result.stream().map(fs->new AccountViewModel(fs.account, accountID, getActivity()).stripLinksFromBio()).collect(Collectors.toList()), false); } }) .exec(accountID); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/SettingsServerAboutFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/SettingsServerAboutFragment.java index f9dd1935..58887ca7 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/SettingsServerAboutFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/SettingsServerAboutFragment.java @@ -128,7 +128,7 @@ public class SettingsServerAboutFragment extends LoaderFragment{ hlp.leftMargin=hlp.rightMargin=V.dp(16); scrollingLayout.addView(heading, hlp); - AccountViewModel model=new AccountViewModel(instance.getContactAccount(), accountID); + AccountViewModel model=new AccountViewModel(instance.getContactAccount(), accountID, getActivity()); AccountViewHolder holder=new AccountViewHolder(this, scrollingLayout, null); holder.setStyle(AccountViewHolder.AccessoryType.NONE, false); holder.bind(model); diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/viewmodel/AccountViewModel.java b/mastodon/src/main/java/org/joinmastodon/android/model/viewmodel/AccountViewModel.java index 978c1d61..0012f033 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/viewmodel/AccountViewModel.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/viewmodel/AccountViewModel.java @@ -1,5 +1,6 @@ package org.joinmastodon.android.model.viewmodel; +import android.content.Context; import android.text.Spannable; import android.text.SpannableStringBuilder; @@ -24,11 +25,11 @@ public class AccountViewModel{ public final CharSequence parsedName, parsedBio; public final String verifiedLink; - public AccountViewModel(Account account, String accountID){ - this(account, accountID, true); + public AccountViewModel(Account account, String accountID, Context context){ + this(account, accountID, true, context); } - public AccountViewModel(Account account, String accountID, boolean needBio){ + public AccountViewModel(Account account, String accountID, boolean needBio, Context context){ this.account=account; avaRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(50), V.dp(50)); emojiHelper=new CustomEmojiHelper(); @@ -38,7 +39,7 @@ public class AccountViewModel{ parsedName=account.displayName; SpannableStringBuilder ssb=new SpannableStringBuilder(parsedName); if(needBio){ - parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID, account); + parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID, account, context); ssb.append(parsedBio); }else{ parsedBio=null; diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/viewmodel/SearchResultViewModel.java b/mastodon/src/main/java/org/joinmastodon/android/model/viewmodel/SearchResultViewModel.java index 4bb8e469..33fa994f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/viewmodel/SearchResultViewModel.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/viewmodel/SearchResultViewModel.java @@ -11,10 +11,10 @@ public class SearchResultViewModel{ public AccountViewModel account; public ListItem hashtagItem; - public SearchResultViewModel(SearchResult result, String accountID, boolean isRecents){ + public SearchResultViewModel(SearchResult result, String accountID, boolean isRecents, Context context){ this.result=result; switch(result.type){ - case ACCOUNT -> account=new AccountViewModel(result.account, accountID); + case ACCOUNT -> account=new AccountViewModel(result.account, accountID, context); case HASHTAG -> { hashtagItem=new ListItem<>((isRecents ? "#" : "")+result.hashtag.name, null, isRecents ? R.drawable.ic_history_24px : R.drawable.ic_tag_24px, null, result.hashtag); hashtagItem.isEnabled=true; diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/AccountStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/AccountStatusDisplayItem.java index 4b76635d..214af28a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/AccountStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/AccountStatusDisplayItem.java @@ -15,7 +15,7 @@ public class AccountStatusDisplayItem extends StatusDisplayItem{ public AccountStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Account account){ super(parentID, parentFragment); - this.account=new AccountViewModel(account, parentFragment.getAccountID()); + this.account=new AccountViewModel(account, parentFragment.getAccountID(), parentFragment.getActivity()); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/InlineStatusStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/InlineStatusStatusDisplayItem.java index 4242d10f..e8d6ad10 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/InlineStatusStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/InlineStatusStatusDisplayItem.java @@ -39,7 +39,7 @@ public class InlineStatusStatusDisplayItem extends StatusDisplayItem{ if(AccountSessionManager.get(parentFragment.getAccountID()).getLocalPreferences().customEmojiInNames) HtmlParser.parseCustomEmoji(parsedName, status.account.emojis); - parsedPostText=HtmlParser.parse(status.content, status.emojis, status.mentions, status.tags, parentFragment.getAccountID(), status.getContentStatus()); + parsedPostText=HtmlParser.parse(status.content, status.emojis, status.mentions, status.tags, parentFragment.getAccountID(), status.getContentStatus(), parentFragment.getActivity()); for(Object span:parsedPostText.getSpans(0, parsedPostText.length(), Object.class)){ if(!(span instanceof CustomEmojiSpan)) parsedPostText.removeSpan(span); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java index a72877d6..b19032bd 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java @@ -141,7 +141,7 @@ public abstract class StatusDisplayItem{ } if(!TextUtils.isEmpty(statusForContent.content)){ - SpannableStringBuilder parsedText=HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID, statusForContent); + SpannableStringBuilder parsedText=HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID, statusForContent, fragment.getActivity()); if(filtered){ HtmlParser.applyFilterHighlights(fragment.getActivity(), parsedText, status.filtered); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java index 4e46f0c9..581dd127 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java @@ -58,7 +58,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{ public void setTranslatedText(String text){ Status statusForContent=status.getContentStatus(); - translatedText=HtmlParser.parse(text, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, parentFragment.getAccountID(), statusForContent); + translatedText=HtmlParser.parse(text, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, parentFragment.getAccountID(), statusForContent, parentFragment.getActivity()); translationEmojiHelper.setText(translatedText); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/text/BaseMonospaceSpan.java b/mastodon/src/main/java/org/joinmastodon/android/ui/text/BaseMonospaceSpan.java new file mode 100644 index 00000000..84fa9ba0 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/text/BaseMonospaceSpan.java @@ -0,0 +1,35 @@ +package org.joinmastodon.android.ui.text; + +import android.content.Context; +import android.text.TextPaint; +import android.text.style.TypefaceSpan; + +import org.joinmastodon.android.R; +import org.joinmastodon.android.ui.utils.UiUtils; + +import androidx.annotation.NonNull; +import me.grishka.appkit.utils.V; + +public abstract class BaseMonospaceSpan extends TypefaceSpan{ + private final Context context; + + public BaseMonospaceSpan(Context context){ + super("monospace"); + this.context=context; + } + + @Override + public void updateDrawState(@NonNull TextPaint paint){ + super.updateDrawState(paint); + paint.setColor(UiUtils.getThemeColor(context, R.attr.colorM3Tertiary)); + paint.setTextSize(paint.getTextSize()*0.9375f); + paint.baselineShift=V.dp(-1); + } + + @Override + public void updateMeasureState(@NonNull TextPaint paint){ + super.updateMeasureState(paint); + paint.setTextSize(paint.getTextSize()*0.9375f); + paint.baselineShift=V.dp(-1); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/text/CodeBlockSpan.java b/mastodon/src/main/java/org/joinmastodon/android/ui/text/CodeBlockSpan.java new file mode 100644 index 00000000..e1cc5187 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/text/CodeBlockSpan.java @@ -0,0 +1,9 @@ +package org.joinmastodon.android.ui.text; + +import android.content.Context; + +public class CodeBlockSpan extends BaseMonospaceSpan{ + public CodeBlockSpan(Context context){ + super(context); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java b/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java index 721a14a4..f72fb1ec 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java @@ -37,6 +37,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import androidx.annotation.NonNull; +import me.grishka.appkit.utils.V; public class HtmlParser{ private static final String TAG="HtmlParser"; @@ -72,7 +73,7 @@ public class HtmlParser{ * @param emojis Custom emojis that are present in source as :code: * @return a spanned string */ - public static SpannableStringBuilder parse(String source, List emojis, List mentions, List tags, String accountID, Object parentObject){ + public static SpannableStringBuilder parse(String source, List emojis, List mentions, List tags, String accountID, Object parentObject, Context context){ class SpanInfo{ public Object span; public int start; @@ -95,10 +96,18 @@ public class HtmlParser{ Jsoup.parseBodyFragment(source).body().traverse(new NodeVisitor(){ private final ArrayList openSpans=new ArrayList<>(); + private boolean isInsidePre(){ + for(SpanInfo si:openSpans){ + if(si.span instanceof CodeBlockSpan) + return true; + } + return false; + } + @Override public void head(@NonNull Node node, int depth){ if(node instanceof TextNode textNode){ - ssb.append(textNode.text()); + ssb.append(isInsidePre() ? textNode.getWholeText().stripTrailing() : textNode.text()); }else if(node instanceof Element el){ switch(el.nodeName()){ case "a" -> { @@ -137,6 +146,15 @@ public class HtmlParser{ case "b", "strong" -> openSpans.add(new SpanInfo(new StyleSpan(Typeface.BOLD), ssb.length(), el)); case "i", "em" -> openSpans.add(new SpanInfo(new StyleSpan(Typeface.ITALIC), ssb.length(), el)); case "s", "del" -> openSpans.add(new SpanInfo(new StrikethroughSpan(), ssb.length(), el)); + case "code" -> { + if(!isInsidePre()){ + openSpans.add(new SpanInfo(new MonospaceSpan(context), ssb.length(), el)); + ssb.append(" ", new SpacerSpan(V.dp(4), 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + case "pre" -> { + openSpans.add(new SpanInfo(new CodeBlockSpan(context), ssb.length(), el)); + } } } } @@ -152,6 +170,9 @@ public class HtmlParser{ }else if(!openSpans.isEmpty()){ SpanInfo si=openSpans.get(openSpans.size()-1); if(si.element==el){ + if(si.span instanceof MonospaceSpan){ + ssb.append(" ", new SpacerSpan(V.dp(4), 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } ssb.setSpan(si.span, si.start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); openSpans.remove(openSpans.size()-1); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/text/MonospaceSpan.java b/mastodon/src/main/java/org/joinmastodon/android/ui/text/MonospaceSpan.java new file mode 100644 index 00000000..ba809819 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/text/MonospaceSpan.java @@ -0,0 +1,9 @@ +package org.joinmastodon.android.ui.text; + +import android.content.Context; + +public class MonospaceSpan extends BaseMonospaceSpan{ + public MonospaceSpan(Context context){ + super(context); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/viewcontrollers/ComposeAutocompleteViewController.java b/mastodon/src/main/java/org/joinmastodon/android/ui/viewcontrollers/ComposeAutocompleteViewController.java index 3afb1859..1575251f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/viewcontrollers/ComposeAutocompleteViewController.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/viewcontrollers/ComposeAutocompleteViewController.java @@ -244,7 +244,7 @@ public class ComposeAutocompleteViewController{ if(mode!=Mode.USERS) return; List oldList=users; - users=result.accounts.stream().map(a->new AccountViewModel(a, accountID)).collect(Collectors.toList()); + users=result.accounts.stream().map(a->new AccountViewModel(a, accountID, activity)).collect(Collectors.toList()); if(isLoading){ isLoading=false; if(users.size()>=LOADING_FAKE_USER_COUNT){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/views/LinkedTextView.java b/mastodon/src/main/java/org/joinmastodon/android/ui/views/LinkedTextView.java index 8e33e877..acc11bc4 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/views/LinkedTextView.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/views/LinkedTextView.java @@ -4,6 +4,10 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.graphics.Canvas; +import android.graphics.CornerPathEffect; +import android.graphics.Paint; +import android.graphics.Path; +import android.text.Layout; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.util.AttributeSet; @@ -14,14 +18,22 @@ import android.view.MenuItem; import android.view.MotionEvent; import android.widget.TextView; +import org.joinmastodon.android.R; import org.joinmastodon.android.ui.text.ClickableLinksDelegate; +import org.joinmastodon.android.ui.text.CodeBlockSpan; import org.joinmastodon.android.ui.text.DeleteWhenCopiedSpan; +import org.joinmastodon.android.ui.text.MonospaceSpan; +import org.joinmastodon.android.ui.utils.UiUtils; + +import me.grishka.appkit.utils.V; public class LinkedTextView extends TextView{ private ClickableLinksDelegate delegate=new ClickableLinksDelegate(this); private boolean needInvalidate; private ActionMode currentActionMode; + private Paint bgPaint=new Paint(Paint.ANTI_ALIAS_FLAG); + private Path tmpPath=new Path(); public LinkedTextView(Context context){ this(context, null); @@ -56,6 +68,9 @@ public class LinkedTextView extends TextView{ currentActionMode=null; } }); + bgPaint.setColor(UiUtils.getThemeColor(context, R.attr.colorM3TertiaryContainer)); + bgPaint.setAlpha(20); + bgPaint.setPathEffect(new CornerPathEffect(V.dp(2))); } public boolean onTouchEvent(MotionEvent ev){ @@ -64,6 +79,22 @@ public class LinkedTextView extends TextView{ } public void onDraw(Canvas c){ + if(getText() instanceof Spanned spanned){ + c.save(); + c.translate(getTotalPaddingLeft(), getTotalPaddingTop()); + Layout layout=getLayout(); + MonospaceSpan[] monospaceSpans=spanned.getSpans(0, spanned.length(), MonospaceSpan.class); + for(MonospaceSpan span:monospaceSpans){ + layout.getSelectionPath(spanned.getSpanStart(span), spanned.getSpanEnd(span), tmpPath); + c.drawPath(tmpPath, bgPaint); + } + CodeBlockSpan[] blockSpans=spanned.getSpans(0, spanned.length(), CodeBlockSpan.class); + for(CodeBlockSpan span:blockSpans){ + c.drawRoundRect(V.dp(-4), layout.getLineTop(layout.getLineForOffset(spanned.getSpanStart(span)))-V.dp(8), layout.getWidth()+V.dp(4), + layout.getLineBottom(layout.getLineForOffset(spanned.getSpanEnd(span)))+V.dp(4), V.dp(2), V.dp(2), bgPaint); + } + c.restore(); + } super.onDraw(c); delegate.onDraw(c); if(needInvalidate)