Notifications

This commit is contained in:
Grishka
2022-03-05 12:59:27 +03:00
parent b437f6f3a3
commit 37bef85f6a
18 changed files with 738 additions and 119 deletions

View File

@@ -0,0 +1,166 @@
package org.joinmastodon.android.ui.displayitems;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Relationship;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.ProgressBarButton;
import java.util.Collections;
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 AccountCardStatusDisplayItem extends StatusDisplayItem{
private final Account account;
public ImageLoaderRequest avaRequest, coverRequest;
public CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
public CharSequence parsedName, parsedBio;
public AccountCardStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Account account){
super(parentID, parentFragment);
this.account=account;
if(!TextUtils.isEmpty(account.avatar))
avaRequest=new UrlImageLoaderRequest(account.avatar, V.dp(50), V.dp(50));
if(!TextUtils.isEmpty(account.header))
coverRequest=new UrlImageLoaderRequest(account.header, 1000, 1000);
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), parentFragment.getAccountID());
if(account.emojis.isEmpty()){
parsedName=account.displayName;
}else{
parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis);
emojiHelper.setText(new SpannableStringBuilder(parsedName).append(parsedBio));
}
}
@Override
public Type getType(){
return Type.ACCOUNT_CARD;
}
@Override
public int getImageCount(){
return 2+emojiHelper.getImageCount();
}
@Override
public ImageLoaderRequest getImageRequest(int index){
return switch(index){
case 0 -> avaRequest;
case 1 -> coverRequest;
default -> emojiHelper.getImageRequest(index-2);
};
}
public static class Holder extends StatusDisplayItem.Holder<AccountCardStatusDisplayItem> implements ImageLoaderViewHolder{
private final ImageView cover, avatar;
private final TextView name, username, bio, followersCount, followingCount, postsCount, followersLabel, followingLabel, postsLabel;
private final ProgressBarButton actionButton;
private final ProgressBar actionProgress;
private final View actionWrap;
private Relationship relationship;
public Holder(Context context, ViewGroup parent){
super(context, R.layout.display_item_account_card, parent);
cover=findViewById(R.id.cover);
avatar=findViewById(R.id.avatar);
name=findViewById(R.id.name);
username=findViewById(R.id.username);
bio=findViewById(R.id.bio);
followersCount=findViewById(R.id.followers_count);
followersLabel=findViewById(R.id.followers_label);
followingCount=findViewById(R.id.following_count);
followingLabel=findViewById(R.id.following_label);
postsCount=findViewById(R.id.posts_count);
postsLabel=findViewById(R.id.posts_label);
actionButton=findViewById(R.id.action_btn);
actionProgress=findViewById(R.id.action_progress);
actionWrap=findViewById(R.id.action_btn_wrap);
View card=findViewById(R.id.card);
card.setOutlineProvider(OutlineProviders.roundedRect(6));
card.setClipToOutline(true);
avatar.setOutlineProvider(OutlineProviders.roundedRect(12));
avatar.setClipToOutline(true);
cover.setOutlineProvider(OutlineProviders.roundedRect(3));
cover.setClipToOutline(true);
actionButton.setOnClickListener(this::onActionButtonClick);
}
@Override
public void onBind(AccountCardStatusDisplayItem item){
name.setText(item.parsedName);
username.setText('@'+item.account.acct);
bio.setText(item.parsedBio);
followersCount.setText(UiUtils.abbreviateNumber(item.account.followersCount));
followingCount.setText(UiUtils.abbreviateNumber(item.account.followingCount));
postsCount.setText(UiUtils.abbreviateNumber(item.account.statusesCount));
followersLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.followers, Math.min(999, item.account.followersCount)));
followingLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.following, Math.min(999, item.account.followingCount)));
postsLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.posts, Math.min(999, item.account.statusesCount)));
relationship=item.parentFragment.getRelationship(item.account.id);
if(relationship==null){
actionWrap.setVisibility(View.GONE);
}else{
actionWrap.setVisibility(View.VISIBLE);
UiUtils.setRelationshipToActionButton(relationship, actionButton);
}
}
private void onActionButtonClick(View v){
itemView.setHasTransientState(true);
UiUtils.performAccountAction((Activity) v.getContext(), item.account, item.parentFragment.getAccountID(), relationship, actionButton, this::setActionProgressVisible, rel->{
itemView.setHasTransientState(false);
item.parentFragment.putRelationship(item.account.id, rel);
rebind();
});
}
private void setActionProgressVisible(boolean visible){
actionButton.setTextVisible(!visible);
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
actionButton.setClickable(!visible);
}
@Override
public void setImage(int index, Drawable image){
if(index==0){
avatar.setImageDrawable(image);
}else if(index==1){
cover.setImageDrawable(image);
}else{
item.emojiHelper.setImageDrawable(index-2, image);
name.invalidate();
bio.invalidate();
}
if(image instanceof Animatable && !((Animatable) image).isRunning())
((Animatable) image).start();
}
@Override
public void clearImage(int index){
setImage(index, null);
}
}
}

View File

@@ -45,8 +45,9 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
public final Status status;
private boolean hasVisibilityToggle;
boolean needBottomPadding;
private String extraText;
public HeaderStatusDisplayItem(String parentID, Account user, Instant createdAt, BaseStatusListFragment parentFragment, String accountID, Status status){
public HeaderStatusDisplayItem(String parentID, Account user, Instant createdAt, BaseStatusListFragment parentFragment, String accountID, Status status, String extraText){
super(parentID, parentFragment);
this.user=user;
this.createdAt=createdAt;
@@ -56,15 +57,18 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
this.status=status;
HtmlParser.parseCustomEmoji(parsedName, user.emojis);
emojiHelper.setText(parsedName);
hasVisibilityToggle=status.sensitive || !TextUtils.isEmpty(status.spoilerText);
if(!hasVisibilityToggle && !status.mediaAttachments.isEmpty()){
for(Attachment att:status.mediaAttachments){
if(att.type!=Attachment.Type.AUDIO){
hasVisibilityToggle=true;
break;
if(status!=null){
hasVisibilityToggle=status.sensitive || !TextUtils.isEmpty(status.spoilerText);
if(!hasVisibilityToggle && !status.mediaAttachments.isEmpty()){
for(Attachment att:status.mediaAttachments){
if(att.type!=Attachment.Type.AUDIO){
hasVisibilityToggle=true;
break;
}
}
}
}
this.extraText=extraText;
}
@Override
@@ -86,7 +90,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
}
public static class Holder extends StatusDisplayItem.Holder<HeaderStatusDisplayItem> implements ImageLoaderViewHolder{
private final TextView name, username, timestamp;
private final TextView name, username, timestamp, extraText;
private final ImageView avatar, more, visibility;
private static final ViewOutlineProvider roundCornersOutline=new ViewOutlineProvider(){
@@ -104,6 +108,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
avatar=findViewById(R.id.avatar);
more=findViewById(R.id.more);
visibility=findViewById(R.id.visibility);
extraText=findViewById(R.id.extra_text);
avatar.setOnClickListener(this::onAvaClick);
avatar.setOutlineProvider(roundCornersOutline);
avatar.setClipToOutline(true);
@@ -121,6 +126,13 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
visibility.setImageResource(item.status.spoilerRevealed ? R.drawable.ic_visibility_off : R.drawable.ic_visibility);
}
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
if(TextUtils.isEmpty(item.extraText)){
extraText.setVisibility(View.GONE);
}else{
extraText.setVisibility(View.VISIBLE);
extraText.setText(item.extraText);
}
more.setVisibility(item.inset ? View.GONE : View.VISIBLE);
}
@Override
@@ -148,11 +160,11 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
}
private void onMoreClick(View v){
Account account=item.status.account;
Account account=item.user;
PopupMenu popup=new PopupMenu(v.getContext(), v);
Menu menu=popup.getMenu();
popup.getMenuInflater().inflate(R.menu.post, menu);
if(!AccountSessionManager.getInstance().isSelf(item.parentFragment.getAccountID(), account))
if(item.status==null || !AccountSessionManager.getInstance().isSelf(item.parentFragment.getAccountID(), account))
menu.findItem(R.id.delete).setVisible(false);
menu.findItem(R.id.mute).setTitle(v.getResources().getString(/*relationship.muting ? R.string.unmute_user :*/ R.string.mute_user, account.displayName));
menu.findItem(R.id.block).setTitle(v.getResources().getString(/*relationship.blocking ? R.string.unblock_user :*/ R.string.block_user, account.displayName));

View File

@@ -29,6 +29,7 @@ import me.grishka.appkit.views.UsableRecyclerView;
public abstract class StatusDisplayItem{
public final String parentID;
public final BaseStatusListFragment parentFragment;
public boolean inset;
public StatusDisplayItem(String parentID, BaseStatusListFragment parentFragment){
this.parentID=parentID;
@@ -58,10 +59,11 @@ public abstract class StatusDisplayItem{
case POLL_FOOTER -> new PollFooterStatusDisplayItem.Holder(activity, parent);
case CARD -> new LinkCardStatusDisplayItem.Holder(activity, parent);
case FOOTER -> new FooterStatusDisplayItem.Holder(activity, parent);
case ACCOUNT_CARD -> new AccountCardStatusDisplayItem.Holder(activity, parent);
};
}
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts){
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter){
String parentID=parentObject.getID();
ArrayList<StatusDisplayItem> items=new ArrayList<>();
Status statusForContent=status.getContentStatus();
@@ -72,7 +74,7 @@ public abstract class StatusDisplayItem{
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));
}
HeaderStatusDisplayItem header;
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent));
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null));
if(!TextUtils.isEmpty(statusForContent.content))
items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID), fragment, statusForContent));
else
@@ -102,10 +104,14 @@ public abstract class StatusDisplayItem{
if(statusForContent.poll!=null){
buildPollItems(parentID, fragment, statusForContent.poll, items);
}
if(statusForContent.card!=null){
if(statusForContent.card!=null && statusForContent.mediaAttachments.isEmpty()){
items.add(new LinkCardStatusDisplayItem(parentID, fragment, statusForContent));
}
items.add(new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID));
if(addFooter){
items.add(new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID));
}
for(StatusDisplayItem item:items)
item.inset=inset;
return items;
}
@@ -128,6 +134,7 @@ public abstract class StatusDisplayItem{
POLL_FOOTER,
CARD,
FOOTER,
ACCOUNT_CARD,
}
public static abstract class Holder<T extends StatusDisplayItem> extends BindableViewHolder<T> implements UsableRecyclerView.Clickable{

View File

@@ -24,10 +24,12 @@ public class HeaderSubtitleLinearLayout extends LinearLayout{
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
if(getChildCount()>1){
if(getLayoutChildCount()>1){
int remainingWidth=MeasureSpec.getSize(widthMeasureSpec);
for(int i=1;i<getChildCount();i++){
View v=getChildAt(i);
if(v.getVisibility()==GONE)
continue;
v.measure(MeasureSpec.getSize(widthMeasureSpec) | MeasureSpec.AT_MOST, heightMeasureSpec);
LayoutParams lp=(LayoutParams) v.getLayoutParams();
remainingWidth-=v.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
@@ -36,7 +38,21 @@ public class HeaderSubtitleLinearLayout extends LinearLayout{
if(first instanceof TextView){
((TextView) first).setMaxWidth(remainingWidth);
}
}else{
View first=getChildAt(0);
if(first instanceof TextView){
((TextView) first).setMaxWidth(Integer.MAX_VALUE);
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private int getLayoutChildCount(){
int count=0;
for(int i=0;i<getChildCount();i++){
if(getChildAt(i).getVisibility()!=GONE)
count++;
}
return count;
}
}