Generalize link card view holder

This commit is contained in:
Grishka 2024-06-15 15:25:19 +03:00
parent 115a6378d3
commit d0352b86e0
7 changed files with 263 additions and 213 deletions

View File

@ -287,8 +287,8 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
outRect.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
}
RecyclerView.ViewHolder holder=list.getChildViewHolder(view);
if(holder instanceof StatusDisplayItem.Holder){
if(((StatusDisplayItem.Holder<?>) holder).getItem().getType()==StatusDisplayItem.Type.GAP){
if(holder instanceof StatusDisplayItem.Holder<?> sih){
if(sih.getItem() instanceof StatusDisplayItem sdi && sdi.getType()==StatusDisplayItem.Type.GAP){
outRect.setEmpty();
return;
}
@ -296,8 +296,8 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
for(int i=0;i<list.getChildCount();i++){
View child=list.getChildAt(i);
holder=list.getChildViewHolder(child);
if(holder instanceof StatusDisplayItem.Holder){
String otherID=((StatusDisplayItem.Holder<?>) holder).getItemID();
if(holder instanceof StatusDisplayItem.Holder<?> sih2){
String otherID=sih2.getItemID();
if(otherID.equals(id)){
list.getDecoratedBoundsWithMargins(child, tmpRect);
outRect.left=Math.min(outRect.left, tmpRect.left);
@ -760,7 +760,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
// Do not draw dividers between hashtag and/or account rows
if((ih instanceof HashtagStatusDisplayItem.Holder || ih instanceof AccountStatusDisplayItem.Holder) && (sh instanceof HashtagStatusDisplayItem.Holder || sh instanceof AccountStatusDisplayItem.Holder))
return false;
return !ih.getItemID().equals(sh.getItemID()) && ih.getItem().getType()!=StatusDisplayItem.Type.GAP;
return !ih.getItemID().equals(sh.getItemID()) && ih.getItem() instanceof StatusDisplayItem sdi && sdi.getType()!=StatusDisplayItem.Type.GAP;
}
return false;
}

View File

@ -71,11 +71,11 @@ public class DiscoverNewsFragment extends BaseRecyclerFragment<CardViewModel> im
@Override
public void onSuccess(List<Card> result){
top3.clear();
top3.addAll(result.subList(0, Math.min(3, result.size())).stream().map(card->new CardViewModel(card, 280, 140)).collect(Collectors.toList()));
top3.addAll(result.subList(0, Math.min(3, result.size())).stream().map(card->new CardViewModel(card, 280, 140, card, accountID)).collect(Collectors.toList()));
cardsAdapter.notifyDataSetChanged();
onDataLoaded(result.subList(top3.size(), result.size()).stream()
.map(card->new CardViewModel(card, 56, 56))
.map(card->new CardViewModel(card, 56, 56, card, accountID))
.collect(Collectors.toList()), false);
bannerHelper.onBannerBecameVisible();
}

View File

@ -1,19 +1,52 @@
package org.joinmastodon.android.model.viewmodel;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Card;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
import me.grishka.appkit.utils.V;
public class CardViewModel{
public final Object parentObject;
public final Card card;
public final ImageLoaderRequest imageRequest;
public final UrlImageLoaderRequest authorAvaRequest;
public final SpannableStringBuilder parsedAuthorName;
public final CustomEmojiHelper authorNameEmojiHelper=new CustomEmojiHelper();
public CardViewModel(Card card, int width, int height){
public CardViewModel(Card card, int width, int height, Object parentObject, String accountID){
this.card=card;
this.parentObject=parentObject;
this.imageRequest=TextUtils.isEmpty(card.image) ? null : new UrlImageLoaderRequest(card.image, V.dp(width), V.dp(height));
if(card.authorAccount!=null){
parsedAuthorName=new SpannableStringBuilder(card.authorAccount.displayName);
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames)
HtmlParser.parseCustomEmoji(parsedAuthorName, card.authorAccount.emojis);
authorNameEmojiHelper.setText(parsedAuthorName);
authorAvaRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? card.authorAccount.avatar : card.authorAccount.avatarStatic, V.dp(50), V.dp(50));
}else{
parsedAuthorName=null;
authorAvaRequest=null;
}
}
public int getImageCount(){
return 1+(card.authorAccount!=null ? (1+authorNameEmojiHelper.getImageCount()) : 0);
}
public ImageLoaderRequest getImageRequest(int index){
return switch(index){
case 0 -> imageRequest;
case 1 -> authorAvaRequest;
default -> authorNameEmojiHelper.getImageRequest(index-2);
};
}
}

View File

@ -1,230 +1,55 @@
package org.joinmastodon.android.ui.displayitems;
import android.annotation.SuppressLint;
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.app.Activity;
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 org.joinmastodon.android.model.viewmodel.CardViewModel;
import org.joinmastodon.android.ui.viewholders.LinkCardHolder;
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{
public class LinkCardStatusDisplayItem extends StatusDisplayItem implements LinkCardHolder.LinkCardProvider{
private final Status status;
private final UrlImageLoaderRequest imgRequest, authorAvaRequest;
private final SpannableStringBuilder parsedAuthorName;
private final CustomEmojiHelper authorNameEmojiHelper=new CustomEmojiHelper();
private final CardViewModel cardViewModel;
public LinkCardStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Status status){
super(parentID, parentFragment);
this.status=status;
if(status.card.image!=null)
imgRequest=new UrlImageLoaderRequest(status.card.image, 1000, 1000);
else
imgRequest=null;
int size=shouldUseLargeCard() ? 1000 : 192;
cardViewModel=new CardViewModel(status.card, size, size, status, parentFragment.getAccountID());
}
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;
}
private boolean shouldUseLargeCard(){
return status.card.type==Card.Type.VIDEO || (status.card.image!=null && status.card.width>status.card.height);
}
@Override
public Type getType(){
return status.card.type==Card.Type.VIDEO || (status.card.image!=null && status.card.width>status.card.height) ? Type.CARD_LARGE : Type.CARD_COMPACT;
return shouldUseLargeCard() ? Type.CARD_LARGE : Type.CARD_COMPACT;
}
@Override
public int getImageCount(){
return 1+(status.card.authorAccount!=null ? (1+authorNameEmojiHelper.getImageCount()) : 0);
return cardViewModel.getImageCount();
}
@Override
public ImageLoaderRequest getImageRequest(int index){
return switch(index){
case 0 -> imgRequest;
case 1 -> authorAvaRequest;
default -> authorNameEmojiHelper.getImageRequest(index-2);
};
return cardViewModel.getImageRequest(index);
}
public static class Holder extends StatusDisplayItem.Holder<LinkCardStatusDisplayItem> implements ImageLoaderViewHolder{
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, authorFooter, authorChip;
private final boolean isLarge;
private final Drawable logoIcon;
@Override
public CardViewModel getCard(){
return cardViewModel;
}
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);
this.isLarge=isLarge;
title=findViewById(R.id.title);
description=findViewById(R.id.description);
domain=findViewById(R.id.domain);
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);
public static class Holder extends LinkCardHolder<LinkCardStatusDisplayItem>{
inner.setOnClickListener(this::onClick);
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")
@Override
public void onBind(LinkCardStatusDisplayItem item){
Card card=item.status.card;
title.setText(card.title);
if(description!=null){
description.setText(card.description);
description.setVisibility(TextUtils.isEmpty(card.description) ? View.GONE : View.VISIBLE);
}
String cardDomain=HtmlParser.normalizeDomain(Objects.requireNonNull(Uri.parse(card.url).getHost()));
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{
authorFooter.setVisibility(View.GONE);
}
if(card.publishedAt!=null){
timestamp.setVisibility(View.VISIBLE);
timestamp.setText(" · "+UiUtils.formatRelativeTimestamp(itemView.getContext(), card.publishedAt));
}else{
timestamp.setVisibility(View.GONE);
}
photo.setImageDrawable(null);
if(item.imgRequest!=null){
photo.setScaleType(ImageView.ScaleType.CENTER_CROP);
photo.setBackground(null);
photo.setImageTintList(null);
crossfadeDrawable.setSize(card.width, card.height);
crossfadeDrawable.setBlurhashDrawable(card.blurhashPlaceholder);
crossfadeDrawable.setCrossfadeAlpha(0f);
photo.setImageDrawable(null);
photo.setImageDrawable(crossfadeDrawable);
didClear=false;
}else{
photo.setBackgroundColor(UiUtils.getThemeColor(itemView.getContext(), R.attr.colorM3SurfaceVariant));
photo.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(itemView.getContext(), R.attr.colorM3Outline)));
photo.setScaleType(ImageView.ScaleType.CENTER);
photo.setImageResource(R.drawable.ic_feed_48px);
}
}
@Override
public void setImage(int index, Drawable drawable){
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){
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);
public Holder(Activity context, ViewGroup parent, boolean isLarge, String accountID){
super(context, parent, isLarge, accountID);
}
}
}

View File

@ -11,6 +11,7 @@ import android.view.ViewGroup;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.StatusListFragment;
import org.joinmastodon.android.fragments.ThreadFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Attachment;
@ -68,8 +69,8 @@ public abstract class StatusDisplayItem{
case AUDIO -> new AudioStatusDisplayItem.Holder(activity, parent);
case POLL_OPTION -> new PollOptionStatusDisplayItem.Holder(activity, parent);
case POLL_FOOTER -> new PollFooterStatusDisplayItem.Holder(activity, parent);
case CARD_LARGE -> new LinkCardStatusDisplayItem.Holder(activity, parent, true);
case CARD_COMPACT -> new LinkCardStatusDisplayItem.Holder(activity, parent, false);
case CARD_LARGE -> new LinkCardStatusDisplayItem.Holder(activity, parent, true, ((StatusListFragment)parentFragment).getAccountID());
case CARD_COMPACT -> new LinkCardStatusDisplayItem.Holder(activity, parent, false, ((StatusListFragment)parentFragment).getAccountID());
case FOOTER -> new FooterStatusDisplayItem.Holder(activity, parent);
case ACCOUNT -> new AccountStatusDisplayItem.Holder(new AccountViewHolder(parentFragment, parent, null));
case HASHTAG -> new HashtagStatusDisplayItem.Holder(activity, parent);
@ -226,7 +227,7 @@ public abstract class StatusDisplayItem{
FILTER_SPOILER
}
public static abstract class Holder<T extends StatusDisplayItem> extends BindableViewHolder<T> implements UsableRecyclerView.DisableableClickable{
public static abstract class Holder<T> extends BindableViewHolder<T> implements UsableRecyclerView.DisableableClickable{
public Holder(View itemView){
super(itemView);
}
@ -236,17 +237,18 @@ public abstract class StatusDisplayItem{
}
public String getItemID(){
return item.parentID;
return item instanceof StatusDisplayItem sdi ? sdi.parentID : null;
}
@Override
public void onClick(){
item.parentFragment.onItemClick(item.parentID);
if(item instanceof StatusDisplayItem sdi)
sdi.parentFragment.onItemClick(sdi.parentID);
}
@Override
public boolean isEnabled(){
return item.parentFragment.isItemEnabled(item.parentID);
return item instanceof StatusDisplayItem sdi && sdi.parentFragment.isItemEnabled(sdi.parentID);
}
}
}

View File

@ -38,7 +38,7 @@ public class InsetStatusItemDecoration extends RecyclerView.ItemDecoration{
View child=parent.getChildAt(i);
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
pos=holder.getAbsoluteAdapterPosition()-listFragment.getMainAdapterOffset();
boolean inset=(holder instanceof StatusDisplayItem.Holder<?> sdi) && sdi.getItem().inset;
boolean inset=holder instanceof StatusDisplayItem.Holder<?> sdi && sdi.getItem() instanceof StatusDisplayItem item && item.inset;
if(inset){
if(rect.isEmpty()){
float childY=child.getY();
@ -80,13 +80,13 @@ public class InsetStatusItemDecoration extends RecyclerView.ItemDecoration{
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
List<StatusDisplayItem> displayItems=listFragment.getDisplayItems();
RecyclerView.ViewHolder holder=parent.getChildViewHolder(view);
if(holder instanceof StatusDisplayItem.Holder<?> sdi){
boolean inset=sdi.getItem().inset;
if(holder instanceof StatusDisplayItem.Holder<?> sdi && sdi.getItem() instanceof StatusDisplayItem item){
boolean inset=item.inset;
int pos=holder.getAbsoluteAdapterPosition()-listFragment.getMainAdapterOffset();
if(inset){
boolean topSiblingInset=pos>0 && displayItems.get(pos-1).inset;
boolean bottomSiblingInset=pos<displayItems.size()-1 && displayItems.get(pos+1).inset;
StatusDisplayItem.Type type=sdi.getItem().getType();
StatusDisplayItem.Type type=item.getType();
if(type==StatusDisplayItem.Type.CARD_LARGE || type==StatusDisplayItem.Type.MEDIA_GRID)
outRect.left=outRect.right=V.dp(16);
else

View File

@ -0,0 +1,190 @@
package org.joinmastodon.android.ui.viewholders;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.model.Card;
import org.joinmastodon.android.model.viewmodel.CardViewModel;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
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.utils.V;
public class LinkCardHolder<T extends LinkCardHolder.LinkCardProvider> extends StatusDisplayItem.Holder<T> implements ImageLoaderViewHolder{
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, authorFooter, authorChip;
private final boolean isLarge;
private final Drawable logoIcon;
private final String accountID;
private Activity activity;
public LinkCardHolder(Activity context, ViewGroup parent, boolean isLarge, String accountID){
super(context, isLarge ? R.layout.display_item_link_card : R.layout.display_item_link_card_compact, parent);
this.isLarge=isLarge;
this.accountID=accountID;
activity=context;
title=findViewById(R.id.title);
description=findViewById(R.id.description);
domain=findViewById(R.id.domain);
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(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")
@Override
public void onBind(T item){
CardViewModel cardVM=item.getCard();
Card card=cardVM.card;
title.setText(card.title);
if(description!=null){
description.setText(card.description);
description.setVisibility(TextUtils.isEmpty(card.description) ? View.GONE : View.VISIBLE);
}
String cardDomain=HtmlParser.normalizeDomain(Objects.requireNonNull(Uri.parse(card.url).getHost()));
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(cardVM.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{
authorFooter.setVisibility(View.GONE);
}
if(card.publishedAt!=null){
timestamp.setVisibility(View.VISIBLE);
timestamp.setText(" · "+UiUtils.formatRelativeTimestamp(itemView.getContext(), card.publishedAt));
}else{
timestamp.setVisibility(View.GONE);
}
photo.setImageDrawable(null);
if(cardVM.imageRequest!=null){
photo.setScaleType(ImageView.ScaleType.CENTER_CROP);
photo.setBackground(null);
photo.setImageTintList(null);
crossfadeDrawable.setSize(card.width, card.height);
crossfadeDrawable.setBlurhashDrawable(card.blurhashPlaceholder);
crossfadeDrawable.setCrossfadeAlpha(0f);
photo.setImageDrawable(null);
photo.setImageDrawable(crossfadeDrawable);
didClear=false;
}else{
photo.setBackgroundColor(UiUtils.getThemeColor(itemView.getContext(), R.attr.colorM3SurfaceVariant));
photo.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(itemView.getContext(), R.attr.colorM3Outline)));
photo.setScaleType(ImageView.ScaleType.CENTER);
photo.setImageResource(R.drawable.ic_feed_48px);
}
}
@Override
public void setImage(int index, Drawable drawable){
if(index==0){
crossfadeDrawable.setImageDrawable(drawable);
if(didClear)
crossfadeDrawable.animateAlpha(0f);
CardViewModel card=item.getCard();
// Make sure the image is not stretched if the server returned wrong dimensions
if(drawable!=null && (drawable.getIntrinsicWidth()!=card.card.width || drawable.getIntrinsicHeight()!=card.card.height)){
photo.setImageDrawable(null);
photo.setImageDrawable(crossfadeDrawable);
}
}else if(index==1){
authorAva.setImageDrawable(drawable);
}else{
item.getCard().authorNameEmojiHelper.setImageDrawable(index-2, drawable);
authorName.invalidate();
}
}
@Override
public void clearImage(int index){
if(index==0){
crossfadeDrawable.setCrossfadeAlpha(1f);
didClear=true;
}else{
setImage(index, null);
}
}
private void onClick(View v){
CardViewModel card=item.getCard();
UiUtils.openURL(itemView.getContext(), accountID, card.card.url, card.parentObject);
}
private void onAuthorChipClick(View v){
Bundle args=new Bundle();
args.putString("account", accountID);
args.putParcelable("profileAccount", Parcels.wrap(item.getCard().card.authorAccount));
Nav.go(activity, ProfileFragment.class, args);
}
public interface LinkCardProvider{
CardViewModel getCard();
}
}