From 115a6378d3f01424603d085c767b9042dde9d587 Mon Sep 17 00:00:00 2001 From: Grishka Date: Sat, 15 Jun 2024 00:32:56 +0300 Subject: [PATCH] `fediverse:creator` in link cards (AND-182) --- .../org/joinmastodon/android/model/Card.java | 3 + .../LinkCardStatusDisplayItem.java | 127 +++++++++++++++--- .../main/res/drawable/bg_link_card_author.xml | 9 ++ .../res/drawable/bg_link_card_author_chip.xml | 9 ++ .../src/main/res/drawable/fg_link_card.xml | 4 +- .../drawable/fg_link_card_author_chip_ava.xml | 5 + .../res/layout/display_item_link_card.xml | 67 +++++---- .../layout/display_item_link_card_compact.xml | 57 ++++---- .../res/layout/link_card_author_footer.xml | 63 +++++++++ 9 files changed, 270 insertions(+), 74 deletions(-) create mode 100644 mastodon/src/main/res/drawable/bg_link_card_author.xml create mode 100644 mastodon/src/main/res/drawable/bg_link_card_author_chip.xml create mode 100644 mastodon/src/main/res/drawable/fg_link_card_author_chip_ava.xml create mode 100644 mastodon/src/main/res/layout/link_card_author_footer.xml diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Card.java b/mastodon/src/main/java/org/joinmastodon/android/model/Card.java index 7eb0ebdd..cda53f47 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Card.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Card.java @@ -36,6 +36,7 @@ public class Card extends BaseModel{ public String blurhash; public List history; public Instant publishedAt; + public Account authorAccount; public transient Drawable blurhashPlaceholder; @@ -49,6 +50,8 @@ public class Card extends BaseModel{ if(placeholder!=null) blurhashPlaceholder=new BlurHashDrawable(placeholder, width, height); } + if(authorAccount!=null) + authorAccount.postprocess(); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/LinkCardStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/LinkCardStatusDisplayItem.java index f02af859..cde38982 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/LinkCardStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/LinkCardStatusDisplayItem.java @@ -5,30 +5,41 @@ import android.content.Context; import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; +import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import org.joinmastodon.android.GlobalUserPreferences; import org.joinmastodon.android.R; +import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.fragments.BaseStatusListFragment; +import org.joinmastodon.android.fragments.ProfileFragment; import org.joinmastodon.android.model.Card; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.ui.OutlineProviders; import org.joinmastodon.android.ui.drawables.BlurhashCrossfadeDrawable; import org.joinmastodon.android.ui.text.HtmlParser; +import org.joinmastodon.android.ui.utils.CustomEmojiHelper; import org.joinmastodon.android.ui.utils.UiUtils; +import org.parceler.Parcels; import java.util.Objects; +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.V; public class LinkCardStatusDisplayItem extends StatusDisplayItem{ private final Status status; - private final UrlImageLoaderRequest imgRequest; + private final UrlImageLoaderRequest imgRequest, authorAvaRequest; + private final SpannableStringBuilder parsedAuthorName; + private final CustomEmojiHelper authorNameEmojiHelper=new CustomEmojiHelper(); public LinkCardStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Status status){ super(parentID, parentFragment); @@ -37,6 +48,17 @@ public class LinkCardStatusDisplayItem extends StatusDisplayItem{ imgRequest=new UrlImageLoaderRequest(status.card.image, 1000, 1000); else imgRequest=null; + + if(status.card.authorAccount!=null){ + parsedAuthorName=new SpannableStringBuilder(status.card.authorAccount.displayName); + if(AccountSessionManager.get(parentFragment.getAccountID()).getLocalPreferences().customEmojiInNames) + HtmlParser.parseCustomEmoji(parsedAuthorName, status.card.authorAccount.emojis); + authorNameEmojiHelper.setText(parsedAuthorName); + authorAvaRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? status.card.authorAccount.avatar : status.card.authorAccount.avatarStatic, V.dp(50), V.dp(50)); + }else{ + parsedAuthorName=null; + authorAvaRequest=null; + } } @Override @@ -46,21 +68,26 @@ public class LinkCardStatusDisplayItem extends StatusDisplayItem{ @Override public int getImageCount(){ - return imgRequest==null ? 0 : 1; + return 1+(status.card.authorAccount!=null ? (1+authorNameEmojiHelper.getImageCount()) : 0); } @Override public ImageLoaderRequest getImageRequest(int index){ - return imgRequest; + return switch(index){ + case 0 -> imgRequest; + case 1 -> authorAvaRequest; + default -> authorNameEmojiHelper.getImageRequest(index-2); + }; } public static class Holder extends StatusDisplayItem.Holder implements ImageLoaderViewHolder{ - private final TextView title, description, domain, timestamp; - private final ImageView photo; + private final TextView title, description, domain, timestamp, authorBefore, authorAfter, authorName; + private final ImageView photo, authorAva; private BlurhashCrossfadeDrawable crossfadeDrawable=new BlurhashCrossfadeDrawable(); private boolean didClear; - private final View inner; + private final View inner, authorFooter, authorChip; private final boolean isLarge; + private final Drawable logoIcon; public Holder(Context context, ViewGroup parent, boolean isLarge){ super(context, isLarge ? R.layout.display_item_link_card : R.layout.display_item_link_card_compact, parent); @@ -71,9 +98,26 @@ public class LinkCardStatusDisplayItem extends StatusDisplayItem{ timestamp=findViewById(R.id.timestamp); photo=findViewById(R.id.photo); inner=findViewById(R.id.inner); + authorBefore=findViewById(R.id.author_before); + authorAfter=findViewById(R.id.author_after); + authorName=findViewById(R.id.author_name); + authorAva=findViewById(R.id.author_ava); + authorChip=findViewById(R.id.author_chip); + authorFooter=findViewById(R.id.author_footer); + inner.setOnClickListener(this::onClick); - inner.setOutlineProvider(OutlineProviders.roundedRect(12)); + inner.setOutlineProvider(OutlineProviders.roundedRect(8)); inner.setClipToOutline(true); + if(!isLarge){ + photo.setOutlineProvider(OutlineProviders.roundedRect(4)); + photo.setClipToOutline(true); + } + authorAva.setOutlineProvider(OutlineProviders.roundedRect(3)); + authorAva.setClipToOutline(true); + authorChip.setOnClickListener(this::onAuthorChipClick); + + logoIcon=context.getResources().getDrawable(R.drawable.ic_ntf_logo, context.getTheme()).mutate(); + logoIcon.setBounds(0, 0, V.dp(17), V.dp(17)); } @SuppressLint("SetTextI18n") @@ -86,11 +130,36 @@ public class LinkCardStatusDisplayItem extends StatusDisplayItem{ description.setVisibility(TextUtils.isEmpty(card.description) ? View.GONE : View.VISIBLE); } String cardDomain=HtmlParser.normalizeDomain(Objects.requireNonNull(Uri.parse(card.url).getHost())); - if(isLarge && !TextUtils.isEmpty(card.authorName)){ - domain.setText(itemView.getContext().getString(R.string.article_by_author, card.authorName)+" · "+cardDomain); + domain.setText(TextUtils.isEmpty(card.providerName) ? cardDomain : card.providerName); + if(card.authorAccount!=null){ + authorFooter.setVisibility(View.VISIBLE); + authorChip.setVisibility(View.VISIBLE); + authorBefore.setVisibility(View.VISIBLE); + String[] authorParts=itemView.getContext().getString(R.string.article_by_author, "{author}").split("\\{author\\}"); + String before=authorParts[0].trim(); + String after=authorParts.length>1 ? authorParts[1].trim() : ""; + if(!TextUtils.isEmpty(before)){ + authorBefore.setText(before); + } + if(TextUtils.isEmpty(after)){ + authorAfter.setVisibility(View.GONE); + }else{ + authorAfter.setVisibility(View.VISIBLE); + authorAfter.setText(after); + } + authorName.setText(item.parsedAuthorName); + authorBefore.setCompoundDrawablesRelative(logoIcon, null, null, null); + }else if(!TextUtils.isEmpty(card.authorName)){ + authorFooter.setVisibility(View.VISIBLE); + authorBefore.setVisibility(View.VISIBLE); + authorBefore.setCompoundDrawables(null, null, null, null); + authorChip.setVisibility(View.GONE); + authorAfter.setVisibility(View.GONE); + authorBefore.setText(itemView.getContext().getString(R.string.article_by_author, card.authorName)); }else{ - domain.setText(cardDomain); + authorFooter.setVisibility(View.GONE); } + if(card.publishedAt!=null){ timestamp.setVisibility(View.VISIBLE); timestamp.setText(" · "+UiUtils.formatRelativeTimestamp(itemView.getContext(), card.publishedAt)); @@ -119,25 +188,43 @@ public class LinkCardStatusDisplayItem extends StatusDisplayItem{ @Override public void setImage(int index, Drawable drawable){ - crossfadeDrawable.setImageDrawable(drawable); - if(didClear) - crossfadeDrawable.animateAlpha(0f); - Card card=item.status.card; - // Make sure the image is not stretched if the server returned wrong dimensions - if(drawable!=null && (drawable.getIntrinsicWidth()!=card.width || drawable.getIntrinsicHeight()!=card.height)){ - photo.setImageDrawable(null); - photo.setImageDrawable(crossfadeDrawable); + if(index==0){ + crossfadeDrawable.setImageDrawable(drawable); + if(didClear) + crossfadeDrawable.animateAlpha(0f); + Card card=item.status.card; + // Make sure the image is not stretched if the server returned wrong dimensions + if(drawable!=null && (drawable.getIntrinsicWidth()!=card.width || drawable.getIntrinsicHeight()!=card.height)){ + photo.setImageDrawable(null); + photo.setImageDrawable(crossfadeDrawable); + } + }else if(index==1){ + authorAva.setImageDrawable(drawable); + }else{ + item.authorNameEmojiHelper.setImageDrawable(index-2, drawable); + authorName.invalidate(); } } @Override public void clearImage(int index){ - crossfadeDrawable.setCrossfadeAlpha(1f); - didClear=true; + if(index==0){ + crossfadeDrawable.setCrossfadeAlpha(1f); + didClear=true; + }else{ + setImage(index, null); + } } private void onClick(View v){ UiUtils.openURL(itemView.getContext(), item.parentFragment.getAccountID(), item.status.card.url, item.status); } + + private void onAuthorChipClick(View v){ + Bundle args=new Bundle(); + args.putString("account", item.parentFragment.getAccountID()); + args.putParcelable("profileAccount", Parcels.wrap(item.status.card.authorAccount)); + Nav.go(item.parentFragment.getActivity(), ProfileFragment.class, args); + } } } diff --git a/mastodon/src/main/res/drawable/bg_link_card_author.xml b/mastodon/src/main/res/drawable/bg_link_card_author.xml new file mode 100644 index 00000000..37812a46 --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_link_card_author.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/bg_link_card_author_chip.xml b/mastodon/src/main/res/drawable/bg_link_card_author_chip.xml new file mode 100644 index 00000000..eb3f7f2a --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_link_card_author_chip.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/fg_link_card.xml b/mastodon/src/main/res/drawable/fg_link_card.xml index 5b4f100f..1d837b3e 100644 --- a/mastodon/src/main/res/drawable/fg_link_card.xml +++ b/mastodon/src/main/res/drawable/fg_link_card.xml @@ -2,13 +2,13 @@ - + - + diff --git a/mastodon/src/main/res/drawable/fg_link_card_author_chip_ava.xml b/mastodon/src/main/res/drawable/fg_link_card_author_chip_ava.xml new file mode 100644 index 00000000..2b61cfbe --- /dev/null +++ b/mastodon/src/main/res/drawable/fg_link_card_author_chip_ava.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/layout/display_item_link_card.xml b/mastodon/src/main/res/layout/display_item_link_card.xml index e58d64f4..b38fa5a0 100644 --- a/mastodon/src/main/res/layout/display_item_link_card.xml +++ b/mastodon/src/main/res/layout/display_item_link_card.xml @@ -14,7 +14,10 @@ android:layout_gravity="center_horizontal" android:orientation="vertical" android:foreground="@drawable/fg_link_card" - android:padding="1dp" + android:paddingHorizontal="1dp" + android:paddingTop="1dp" + android:paddingBottom="16dp" + android:clipToPadding="false" android:maxWidth="400dp"> + + + + + + @@ -45,33 +76,13 @@ android:textAppearance="@style/m3_body_medium" android:textColor="?colorM3OnSurface" tools:text="Link description"/> - - - - + android:layout_marginBottom="-16dp"/> \ No newline at end of file diff --git a/mastodon/src/main/res/layout/display_item_link_card_compact.xml b/mastodon/src/main/res/layout/display_item_link_card_compact.xml index 3cc79302..aface938 100644 --- a/mastodon/src/main/res/layout/display_item_link_card_compact.xml +++ b/mastodon/src/main/res/layout/display_item_link_card_compact.xml @@ -10,7 +10,7 @@ - - + android:layout_marginStart="12dp" + android:layout_marginTop="12dp" + android:layout_marginBottom="2dp"> + + \ No newline at end of file diff --git a/mastodon/src/main/res/layout/link_card_author_footer.xml b/mastodon/src/main/res/layout/link_card_author_footer.xml new file mode 100644 index 00000000..fe60c830 --- /dev/null +++ b/mastodon/src/main/res/layout/link_card_author_footer.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + \ No newline at end of file