diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java index ae63ebfb..679b6cc0 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -12,6 +12,7 @@ import android.view.ViewGroup; import android.widget.ImageView; import org.joinmastodon.android.R; +import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.DisplayItemsParent; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem; @@ -22,6 +23,7 @@ import org.joinmastodon.android.ui.photoviewer.PhotoViewer; import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import androidx.annotation.NonNull; @@ -40,6 +42,7 @@ public abstract class BaseStatusListFragment exten protected DisplayItemsAdapter adapter; protected String accountID; protected PhotoViewer currentPhotoViewer; + protected HashMap knownAccounts=new HashMap<>(); public BaseStatusListFragment(){ super(20); @@ -59,6 +62,9 @@ public abstract class BaseStatusListFragment exten @Override public void onAppendItems(List items){ super.onAppendItems(items); + for(T s:items){ + addAccountToKnown(s); + } for(T s:items){ displayItems.addAll(buildDisplayItems(s)); } @@ -73,6 +79,9 @@ public abstract class BaseStatusListFragment exten protected void prependItems(List items){ data.addAll(0, items); int offset=0; + for(T s:items){ + addAccountToKnown(s); + } for(T s:items){ List toAdd=buildDisplayItems(s); displayItems.addAll(offset, toAdd); @@ -91,6 +100,7 @@ public abstract class BaseStatusListFragment exten } protected abstract List buildDisplayItems(T s); + protected abstract void addAccountToKnown(T s); @Override protected void onHidden(){ @@ -324,5 +334,15 @@ public abstract class BaseStatusListFragment exten public ImageLoaderRequest getImageRequest(int position, int image){ return displayItems.get(position).getImageRequest(image); } + + @Override + public void onViewDetachedFromWindow(@NonNull BindableViewHolder holder){ + if(holder instanceof ImageLoaderViewHolder){ + int count=holder.getItem().getImageCount(); + for(int i=0;i{ case FAVORITE -> getString(R.string.user_favorited, n.account.displayName); case POLL -> getString(R.string.poll_ended); case STATUS -> getString(R.string.user_posted, n.account.displayName); - }); + }, n.account.emojis, R.drawable.ic_fluent_arrow_reply_20_filled); if(n.status!=null){ - ArrayList items=StatusDisplayItem.buildItems(this, n.status, accountID, n); + ArrayList items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts); items.add(0, titleItem); return items; }else{ @@ -41,6 +41,14 @@ public class NotificationsFragment extends BaseStatusListFragment{ } } + @Override + protected void addAccountToKnown(Notification s){ + if(!knownAccounts.containsKey(s.account.id)) + knownAccounts.put(s.account.id, s.account); + if(s.status!=null && !knownAccounts.containsKey(s.status.account.id)) + knownAccounts.put(s.status.account.id, s.status.account); + } + @Override protected void doLoadData(int offset, int count){ new GetNotifications(offset>0 ? getMaxID() : null, count) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java index f4306014..f822908f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java @@ -16,7 +16,13 @@ import androidx.recyclerview.widget.RecyclerView; public abstract class StatusListFragment extends BaseStatusListFragment{ protected List buildDisplayItems(Status s){ - return StatusDisplayItem.buildItems(this, s, accountID, s); + return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts); + } + + @Override + protected void addAccountToKnown(Status s){ + if(!knownAccounts.containsKey(s.account.id)) + knownAccounts.put(s.account.id, s.account); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/CustomEmojiHelper.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/CustomEmojiHelper.java new file mode 100644 index 00000000..a9efc97e --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/CustomEmojiHelper.java @@ -0,0 +1,44 @@ +package org.joinmastodon.android.ui.displayitems; + +import android.graphics.drawable.Drawable; +import android.text.Spanned; + +import org.joinmastodon.android.ui.text.CustomEmojiSpan; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import me.grishka.appkit.imageloader.requests.ImageLoaderRequest; + +class CustomEmojiHelper{ + public List> spans=new ArrayList<>(); + public List requests=new ArrayList<>(); + + public void setText(CharSequence text){ + spans.clear(); + requests.clear(); + if(!(text instanceof Spanned)) + return; + CustomEmojiSpan[] spans=((Spanned) text).getSpans(0, text.length(), CustomEmojiSpan.class); + for(List group:Arrays.stream(spans).collect(Collectors.groupingBy(s->s.emoji)).values()){ + this.spans.add(group); + requests.add(group.get(0).createImageLoaderRequest()); + } + } + + public int getImageCount(){ + return requests.size(); + } + + public ImageLoaderRequest getImageRequest(int image){ + return requests.get(image); + } + + public void setImageDrawable(int image, Drawable drawable){ + for(CustomEmojiSpan span:spans.get(image)){ + span.setDrawable(drawable); + } + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java index b8839db3..90397070 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java @@ -7,7 +7,6 @@ import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.SpannableStringBuilder; -import android.text.Spanned; import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; @@ -18,7 +17,6 @@ import org.joinmastodon.android.R; import org.joinmastodon.android.fragments.BaseStatusListFragment; import org.joinmastodon.android.fragments.ProfileFragment; import org.joinmastodon.android.model.Account; -import org.joinmastodon.android.ui.text.CustomEmojiSpan; import org.joinmastodon.android.ui.text.HtmlParser; import org.joinmastodon.android.ui.utils.UiUtils; import org.parceler.Parcels; @@ -29,7 +27,6 @@ import me.grishka.appkit.Nav; import me.grishka.appkit.imageloader.ImageLoaderViewHolder; import me.grishka.appkit.imageloader.requests.ImageLoaderRequest; import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest; -import me.grishka.appkit.utils.BindableViewHolder; import me.grishka.appkit.utils.V; public class HeaderStatusDisplayItem extends StatusDisplayItem{ @@ -38,8 +35,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{ private ImageLoaderRequest avaRequest; private Fragment parentFragment; private String accountID; - private ImageLoaderRequest[] emojiRequests; - private CustomEmojiSpan[] emojiSpans; + private CustomEmojiHelper emojiHelper=new CustomEmojiHelper(); private SpannableStringBuilder parsedName; public HeaderStatusDisplayItem(String parentID, Account user, Instant createdAt, BaseStatusListFragment parentFragment, String accountID){ @@ -51,11 +47,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{ this.accountID=accountID; parsedName=new SpannableStringBuilder(user.displayName); HtmlParser.parseCustomEmoji(parsedName, user.emojis); - emojiSpans=parsedName.getSpans(0, parsedName.length(), CustomEmojiSpan.class); - emojiRequests=new ImageLoaderRequest[emojiSpans.length]; - for(int i=0; i0){ - return emojiRequests[index-1]; + return emojiHelper.getImageRequest(index-1); } return avaRequest; } @@ -110,7 +102,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{ @Override public void setImage(int index, Drawable drawable){ if(index>0){ - item.emojiSpans[index-1].setDrawable(drawable); + item.emojiHelper.setImageDrawable(index-1, drawable); }else{ avatar.setImageDrawable(drawable); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ReblogOrReplyLineStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ReblogOrReplyLineStatusDisplayItem.java index 34346fd1..79acc8c7 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ReblogOrReplyLineStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ReblogOrReplyLineStatusDisplayItem.java @@ -1,22 +1,37 @@ package org.joinmastodon.android.ui.displayitems; import android.app.Activity; +import android.graphics.drawable.Drawable; import android.os.Build; +import android.text.SpannableStringBuilder; import android.view.ViewGroup; import android.widget.TextView; import org.joinmastodon.android.R; import org.joinmastodon.android.fragments.BaseStatusListFragment; +import org.joinmastodon.android.model.Emoji; +import org.joinmastodon.android.ui.text.HtmlParser; import org.joinmastodon.android.ui.utils.UiUtils; -import me.grishka.appkit.utils.BindableViewHolder; +import java.util.List; + +import androidx.annotation.DrawableRes; +import me.grishka.appkit.imageloader.ImageLoaderViewHolder; +import me.grishka.appkit.imageloader.requests.ImageLoaderRequest; public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{ private CharSequence text; + @DrawableRes + private int icon; + private CustomEmojiHelper emojiHelper=new CustomEmojiHelper(); - public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text){ + public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List emojis, @DrawableRes int icon){ super(parentID, parentFragment); - this.text=text; + SpannableStringBuilder ssb=new SpannableStringBuilder(text); + HtmlParser.parseCustomEmoji(ssb, emojis); + this.text=ssb; + emojiHelper.setText(ssb); + this.icon=icon; } @Override @@ -24,7 +39,17 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{ return Type.REBLOG_OR_REPLY_LINE; } - public static class Holder extends StatusDisplayItem.Holder{ + @Override + public int getImageCount(){ + return emojiHelper.getImageCount(); + } + + @Override + public ImageLoaderRequest getImageRequest(int index){ + return emojiHelper.getImageRequest(index); + } + + public static class Holder extends StatusDisplayItem.Holder implements ImageLoaderViewHolder{ private final TextView text; public Holder(Activity activity, ViewGroup parent){ super(activity, R.layout.display_item_reblog_or_reply_line, parent); @@ -36,6 +61,17 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{ @Override public void onBind(ReblogOrReplyLineStatusDisplayItem item){ text.setText(item.text); + text.setCompoundDrawablesRelativeWithIntrinsicBounds(item.icon, 0, 0, 0); + } + + @Override + public void setImage(int index, Drawable image){ + item.emojiHelper.setImageDrawable(index, image); + } + + @Override + public void clearImage(int index){ + setImage(index, null); } } } 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 cb04733d..8d5dc757 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 @@ -1,7 +1,6 @@ package org.joinmastodon.android.ui.displayitems; import android.app.Activity; -import android.app.Fragment; import android.content.Context; import android.text.TextUtils; import android.view.View; @@ -9,12 +8,15 @@ import android.view.ViewGroup; import org.joinmastodon.android.R; import org.joinmastodon.android.fragments.BaseStatusListFragment; +import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.Attachment; import org.joinmastodon.android.model.DisplayItemsParent; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.ui.text.HtmlParser; import java.util.ArrayList; +import java.util.Map; +import java.util.Objects; import me.grishka.appkit.imageloader.requests.ImageLoaderRequest; import me.grishka.appkit.utils.BindableViewHolder; @@ -51,12 +53,15 @@ public abstract class StatusDisplayItem{ }; } - public static ArrayList buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject){ + public static ArrayList buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map knownAccounts){ String parentID=parentObject.getID(); ArrayList items=new ArrayList<>(); Status statusForContent=status.getContentStatus(); if(status.reblog!=null){ - items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.user_boosted, status.account.displayName))); + items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.user_boosted, status.account.displayName), status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20_filled)); + }else if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId)){ + Account account=Objects.requireNonNull(knownAccounts.get(status.inReplyToAccountId)); + items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.in_reply_to, account.displayName), account.emojis, R.drawable.ic_fluent_arrow_reply_20_filled)); } items.add(new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID)); if(!TextUtils.isEmpty(statusForContent.content)) 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 33ef8b67..218fbc95 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 @@ -4,41 +4,25 @@ import android.app.Activity; import android.app.Fragment; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; -import android.text.Spanned; import android.view.ViewGroup; -import android.widget.Toast; import org.joinmastodon.android.R; import org.joinmastodon.android.fragments.BaseStatusListFragment; -import org.joinmastodon.android.ui.text.CustomEmojiSpan; -import org.joinmastodon.android.ui.text.LinkSpan; -import org.joinmastodon.android.ui.utils.UiUtils; import org.joinmastodon.android.ui.views.LinkedTextView; import me.grishka.appkit.imageloader.ImageLoaderViewHolder; import me.grishka.appkit.imageloader.MovieDrawable; import me.grishka.appkit.imageloader.requests.ImageLoaderRequest; -import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest; -import me.grishka.appkit.utils.BindableViewHolder; -import me.grishka.appkit.utils.V; public class TextStatusDisplayItem extends StatusDisplayItem{ private CharSequence text; - private ImageLoaderRequest[] emojiRequests; + private CustomEmojiHelper emojiHelper=new CustomEmojiHelper(); private Fragment parentFragment; public TextStatusDisplayItem(String parentID, CharSequence text, BaseStatusListFragment parentFragment){ super(parentID, parentFragment); this.text=text; this.parentFragment=parentFragment; - if(text instanceof Spanned){ - CustomEmojiSpan[] emojiSpans=((Spanned) text).getSpans(0, text.length(), CustomEmojiSpan.class); - emojiRequests=new ImageLoaderRequest[emojiSpans.length]; - for(int i=0; i implements ImageLoaderViewHolder{ private final LinkedTextView text; - private CustomEmojiSpan[] emojiSpans; public Holder(Activity activity, ViewGroup parent){ super(activity, R.layout.display_item_text, parent); @@ -68,21 +51,12 @@ public class TextStatusDisplayItem extends StatusDisplayItem{ @Override public void onBind(TextStatusDisplayItem item){ text.setText(item.text); - if(emojiSpans!=null){ - for(CustomEmojiSpan span:emojiSpans){ - span.setDrawable(null); - } - } - if(item.text instanceof Spanned) - emojiSpans=((Spanned) item.text).getSpans(0, item.text.length(), CustomEmojiSpan.class); - else - emojiSpans=new CustomEmojiSpan[0]; text.setInvalidateOnEveryFrame(false); } @Override public void setImage(int index, Drawable image){ - emojiSpans[index].setDrawable(image); + item.emojiHelper.setImageDrawable(index, image); text.invalidate(); if(image instanceof Animatable){ ((Animatable) image).start(); @@ -93,7 +67,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{ @Override public void clearImage(int index){ - emojiSpans[index].setDrawable(null); + item.emojiHelper.setImageDrawable(index, null); text.invalidate(); } }