Better toot layouts, char counter in compose

This commit is contained in:
Grishka
2022-02-01 08:56:13 +03:00
parent a4a514d37a
commit b9bdf7caec
33 changed files with 2744 additions and 140 deletions

View File

@@ -0,0 +1,68 @@
package org.joinmastodon.android.ui.displayitems;
import android.app.Activity;
import android.content.res.ColorStateList;
import android.os.Build;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.text.DecimalFormat;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.V;
public class FooterStatusDisplayItem extends StatusDisplayItem{
private final Status status;
private final String accountID;
public FooterStatusDisplayItem(String parentID, Status status, String accountID){
super(parentID);
this.status=status;
this.accountID=accountID;
}
@Override
public Type getType(){
return Type.FOOTER;
}
public static class Holder extends BindableViewHolder<FooterStatusDisplayItem>{
private final TextView reply, boost, favorite;
private final ImageView share;
public Holder(Activity activity, ViewGroup parent){
super(activity, R.layout.display_item_footer, parent);
reply=findViewById(R.id.reply);
boost=findViewById(R.id.boost);
favorite=findViewById(R.id.favorite);
share=findViewById(R.id.share);
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N){
UiUtils.fixCompoundDrawableTintOnAndroid6(reply, R.color.text_secondary);
UiUtils.fixCompoundDrawableTintOnAndroid6(boost, R.color.text_secondary);
UiUtils.fixCompoundDrawableTintOnAndroid6(favorite, R.color.text_secondary);
}
}
@Override
public void onBind(FooterStatusDisplayItem item){
bindButton(reply, item.status.repliesCount);
bindButton(boost, item.status.reblogsCount);
bindButton(favorite, item.status.favouritesCount);
}
private void bindButton(TextView btn, int count){
if(count>0){
btn.setText(DecimalFormat.getIntegerInstance().format(count));
btn.setCompoundDrawablePadding(V.dp(8));
}else{
btn.setText("");
btn.setCompoundDrawablePadding(0);
}
}
}
}

View File

@@ -2,18 +2,21 @@ package org.joinmastodon.android.ui.displayitems;
import android.app.Activity;
import android.app.Fragment;
import android.graphics.Outline;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.parceler.Parcels;
import java.time.Instant;
@@ -23,6 +26,7 @@ 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{
private Account user;
@@ -56,20 +60,34 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
}
public static class Holder extends BindableViewHolder<HeaderStatusDisplayItem> implements ImageLoaderViewHolder{
private final TextView name, subtitle;
private final ImageView avatar;
private final TextView name, username, timestamp;
private final ImageView avatar, more;
private static final ViewOutlineProvider roundCornersOutline=new ViewOutlineProvider(){
@Override
public void getOutline(View view, Outline outline){
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), V.dp(12));
}
};
public Holder(Activity activity, ViewGroup parent){
super(activity, R.layout.display_item_header, parent);
name=findViewById(R.id.name);
subtitle=findViewById(R.id.subtitle);
username=findViewById(R.id.username);
timestamp=findViewById(R.id.timestamp);
avatar=findViewById(R.id.avatar);
more=findViewById(R.id.more);
avatar.setOnClickListener(this::onAvaClick);
avatar.setOutlineProvider(roundCornersOutline);
avatar.setClipToOutline(true);
more.setOnClickListener(this::onMoreClick);
}
@Override
public void onBind(HeaderStatusDisplayItem item){
name.setText(item.user.displayName);
subtitle.setText('@'+item.user.acct);
username.setText('@'+item.user.acct);
timestamp.setText(UiUtils.formatRelativeTimestamp(itemView.getContext(), item.createdAt));
}
@Override
@@ -90,5 +108,9 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
args.putParcelable("profileAccount", Parcels.wrap(item.user));
Nav.go(item.parentFragment.getActivity(), ProfileFragment.class, args);
}
private void onMoreClick(View v){
}
}
}

View File

@@ -1,11 +1,12 @@
package org.joinmastodon.android.ui.displayitems;
import android.app.Activity;
import android.os.Build;
import android.view.ViewGroup;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.utils.UiUtils;
import me.grishka.appkit.utils.BindableViewHolder;
@@ -27,6 +28,8 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
public Holder(Activity activity, ViewGroup parent){
super(activity, R.layout.display_item_reblog_or_reply_line, parent);
text=findViewById(R.id.text);
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N)
UiUtils.fixCompoundDrawableTintOnAndroid6(text, R.color.text_secondary);
}
@Override

View File

@@ -39,6 +39,7 @@ public abstract class StatusDisplayItem{
case REBLOG_OR_REPLY_LINE -> new ReblogOrReplyLineStatusDisplayItem.Holder(activity, parent);
case TEXT -> new TextStatusDisplayItem.Holder(activity, parent);
case PHOTO -> new PhotoStatusDisplayItem.Holder(activity, parent);
case FOOTER -> new FooterStatusDisplayItem.Holder(activity, parent);
default -> throw new UnsupportedOperationException();
};
}
@@ -58,6 +59,7 @@ public abstract class StatusDisplayItem{
items.add(new PhotoStatusDisplayItem(parentID, status, attachment, fragment));
}
}
items.add(new FooterStatusDisplayItem(parentID, status, accountID));
return items;
}

View File

@@ -1,8 +1,17 @@
package org.joinmastodon.android.ui.utils;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.Log;
import android.widget.TextView;
import org.joinmastodon.android.R;
import java.time.Instant;
import androidx.annotation.ColorRes;
import androidx.browser.customtabs.CustomTabsIntent;
public class UiUtils{
@@ -14,4 +23,37 @@ public class UiUtils{
.build()
.launchUrl(context, Uri.parse(url));
}
public static String formatRelativeTimestamp(Context context, Instant instant){
long t=instant.toEpochMilli();
long now=System.currentTimeMillis();
long diff=now-t;
if(diff<60_000L){
return context.getString(R.string.time_seconds, diff/1000L);
}else if(diff<3600_000L){
return context.getString(R.string.time_minutes, diff/60_000L);
}else if(diff<3600_000L*24L){
return context.getString(R.string.time_hours, diff/3600_000L);
}else{
return context.getString(R.string.time_days, diff/(3600_000L*24L));
}
}
/**
* Android 6.0 has a bug where start and end compound drawables don't get tinted.
* This works around it by setting the tint colors directly to the drawables.
* @param textView
* @param color
*/
public static void fixCompoundDrawableTintOnAndroid6(TextView textView, @ColorRes int color){
Drawable[] drawables=textView.getCompoundDrawablesRelative();
for(int i=0;i<drawables.length;i++){
if(drawables[i]!=null){
Drawable tinted=drawables[i].mutate();
tinted.setTintList(textView.getContext().getColorStateList(color));
drawables[i]=tinted;
}
}
textView.setCompoundDrawablesRelative(drawables[0], drawables[1], drawables[2], drawables[3]);
}
}

View File

@@ -0,0 +1,42 @@
package org.joinmastodon.android.ui.views;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* A LinearLayout for TextViews. First child TextView will get truncated if it doesn't fit, remaining will always wrap content.
*/
public class HeaderSubtitleLinearLayout extends LinearLayout{
public HeaderSubtitleLinearLayout(Context context){
super(context);
}
public HeaderSubtitleLinearLayout(Context context, AttributeSet attrs){
super(context, attrs);
}
public HeaderSubtitleLinearLayout(Context context, AttributeSet attrs, int defStyleAttr){
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
if(getChildCount()>1){
int remainingWidth=MeasureSpec.getSize(widthMeasureSpec);
for(int i=1;i<getChildCount();i++){
View v=getChildAt(i);
v.measure(MeasureSpec.getSize(widthMeasureSpec) | MeasureSpec.AT_MOST, heightMeasureSpec);
LayoutParams lp=(LayoutParams) v.getLayoutParams();
remainingWidth-=v.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
}
View first=getChildAt(0);
if(first instanceof TextView){
((TextView) first).setMaxWidth(remainingWidth);
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}