Reporting M3 redesign
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
package org.joinmastodon.android.ui.displayitems;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.views.CheckableRelativeLayout;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class CheckableHeaderStatusDisplayItem extends HeaderStatusDisplayItem{
|
||||
public CheckableHeaderStatusDisplayItem(String parentID, Account user, Instant createdAt, BaseStatusListFragment parentFragment, String accountID, Status status, String extraText){
|
||||
super(parentID, user, createdAt, parentFragment, accountID, status, extraText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(){
|
||||
return Type.HEADER_CHECKABLE;
|
||||
}
|
||||
|
||||
public static class Holder extends HeaderStatusDisplayItem.Holder{
|
||||
private final View checkbox;
|
||||
private final CheckableRelativeLayout view;
|
||||
private Predicate<Holder> isChecked;
|
||||
|
||||
public Holder(Activity activity, ViewGroup parent){
|
||||
super(activity, R.layout.display_item_header_checkable, parent);
|
||||
checkbox=findViewById(R.id.checkbox);
|
||||
view=(CheckableRelativeLayout) itemView;
|
||||
checkbox.setBackground(new CheckBox(activity).getButtonDrawable());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(HeaderStatusDisplayItem item){
|
||||
super.onBind(item);
|
||||
if(isChecked!=null){
|
||||
view.setChecked(isChecked.test(this));
|
||||
}
|
||||
}
|
||||
|
||||
public void setIsChecked(Predicate<Holder> isChecked){
|
||||
this.isChecked=isChecked;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,16 @@ package org.joinmastodon.android.ui.displayitems;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
@@ -43,6 +41,7 @@ import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.APIRequest;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
@@ -114,7 +113,11 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||
private APIRequest<?> currentRelationshipRequest;
|
||||
|
||||
public Holder(Activity activity, ViewGroup parent){
|
||||
super(activity, R.layout.display_item_header, parent);
|
||||
this(activity, R.layout.display_item_header, parent);
|
||||
}
|
||||
|
||||
protected Holder(Activity activity, @LayoutRes int layout, ViewGroup parent){
|
||||
super(activity, layout, parent);
|
||||
name=findViewById(R.id.name);
|
||||
timeAndUsername=findViewById(R.id.time_and_username);
|
||||
avatar=findViewById(R.id.avatar);
|
||||
@@ -165,6 +168,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||
args.putString("account", item.parentFragment.getAccountID());
|
||||
args.putParcelable("status", Parcels.wrap(item.status));
|
||||
args.putParcelable("reportAccount", Parcels.wrap(item.status.account));
|
||||
args.putParcelable("relationship", Parcels.wrap(relationship));
|
||||
Nav.go(item.parentFragment.getActivity(), ReportReasonChoiceFragment.class, args);
|
||||
}else if(id==R.id.open_in_browser){
|
||||
UiUtils.launchWebBrowser(activity, item.status.url);
|
||||
|
||||
@@ -48,6 +48,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
||||
private final ArrayList<ImageLoaderRequest> requests=new ArrayList<>();
|
||||
public final Status status;
|
||||
public boolean sensitiveRevealed;
|
||||
public String sensitiveTitle;
|
||||
|
||||
public MediaGridStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, PhotoLayoutHelper.TiledLayoutResult tiledLayout, List<Attachment> attachments, Status status){
|
||||
super(parentID, parentFragment);
|
||||
@@ -103,6 +104,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
||||
private final LayerDrawable sensitiveOverlayBG;
|
||||
private static final ColorDrawable drawableForWhenThereIsNoBlurhash=new ColorDrawable(0xffffffff);
|
||||
private final TextView hideSensitiveButton;
|
||||
private final TextView sensitiveText;
|
||||
|
||||
private int altTextIndex=-1;
|
||||
private Animator altTextAnimator;
|
||||
@@ -112,7 +114,6 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
||||
wrapper=(FrameLayout)itemView;
|
||||
layout=new MediaGridLayout(activity);
|
||||
wrapper.addView(layout);
|
||||
wrapper.setPadding(0, 0, 0, V.dp(8));
|
||||
wrapper.setClipToPadding(false);
|
||||
|
||||
overlays=new MaxWidthFrameLayout(activity);
|
||||
@@ -136,15 +137,20 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
||||
|
||||
activity.getLayoutInflater().inflate(R.layout.overlay_image_sensitive, overlays);
|
||||
sensitiveOverlay=findViewById(R.id.sensitive_overlay);
|
||||
sensitiveOverlayBG=(LayerDrawable) sensitiveOverlay.getBackground();
|
||||
sensitiveOverlayBG=(LayerDrawable) sensitiveOverlay.getBackground().mutate();
|
||||
sensitiveOverlayBG.setDrawableByLayerId(R.id.left_drawable, new SpoilerStripesDrawable(false));
|
||||
sensitiveOverlayBG.setDrawableByLayerId(R.id.right_drawable, new SpoilerStripesDrawable(true));
|
||||
sensitiveOverlay.setBackground(sensitiveOverlayBG);
|
||||
sensitiveOverlay.setOnClickListener(v->revealSensitive());
|
||||
hideSensitiveButton.setOnClickListener(v->hideSensitive());
|
||||
|
||||
sensitiveText=findViewById(R.id.sensitive_text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(MediaGridStatusDisplayItem item){
|
||||
wrapper.setPadding(0, 0, 0, item.inset ? 0 : V.dp(8));
|
||||
|
||||
if(altTextAnimator!=null)
|
||||
altTextAnimator.cancel();
|
||||
|
||||
@@ -190,6 +196,10 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
||||
layout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
hideSensitiveButton.setVisibility(item.status.sensitive ? View.VISIBLE : View.GONE);
|
||||
if(!TextUtils.isEmpty(item.sensitiveTitle))
|
||||
sensitiveText.setText(item.sensitiveTitle);
|
||||
else
|
||||
sensitiveText.setText(R.string.sensitive_content_explain);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -346,5 +356,13 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
||||
item.sensitiveRevealed=false;
|
||||
V.setVisibilityAnimated(sensitiveOverlay, View.VISIBLE, ()->layout.setVisibility(View.INVISIBLE));
|
||||
}
|
||||
|
||||
public MediaGridLayout getLayout(){
|
||||
return layout;
|
||||
}
|
||||
|
||||
public View getSensitiveOverlay(){
|
||||
return sensitiveOverlay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@ public abstract class StatusDisplayItem{
|
||||
public boolean inset;
|
||||
public int index;
|
||||
|
||||
public static final int FLAG_INSET=1;
|
||||
public static final int FLAG_NO_FOOTER=1 << 1;
|
||||
public static final int FLAG_CHECKABLE=1 << 2;
|
||||
public static final int FLAG_MEDIA_FORCE_HIDDEN=1 << 3;
|
||||
|
||||
public StatusDisplayItem(String parentID, BaseStatusListFragment parentFragment){
|
||||
this.parentID=parentID;
|
||||
this.parentFragment=parentFragment;
|
||||
@@ -51,6 +56,7 @@ public abstract class StatusDisplayItem{
|
||||
public static BindableViewHolder<? extends StatusDisplayItem> createViewHolder(Type type, Activity activity, ViewGroup parent){
|
||||
return switch(type){
|
||||
case HEADER -> new HeaderStatusDisplayItem.Holder(activity, parent);
|
||||
case HEADER_CHECKABLE -> new CheckableHeaderStatusDisplayItem.Holder(activity, parent);
|
||||
case REBLOG_OR_REPLY_LINE -> new ReblogOrReplyLineStatusDisplayItem.Holder(activity, parent);
|
||||
case TEXT -> new TextStatusDisplayItem.Holder(activity, parent);
|
||||
case AUDIO -> new AudioStatusDisplayItem.Holder(activity, parent);
|
||||
@@ -70,6 +76,15 @@ public abstract class StatusDisplayItem{
|
||||
}
|
||||
|
||||
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter){
|
||||
int flags=0;
|
||||
if(inset)
|
||||
flags|=FLAG_INSET;
|
||||
if(!addFooter)
|
||||
flags|=FLAG_NO_FOOTER;
|
||||
return buildItems(fragment, status, accountID, parentObject, knownAccounts, flags);
|
||||
}
|
||||
|
||||
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, int flags){
|
||||
String parentID=parentObject.getID();
|
||||
ArrayList<StatusDisplayItem> items=new ArrayList<>();
|
||||
Status statusForContent=status.getContentStatus();
|
||||
@@ -80,7 +95,10 @@ public abstract class StatusDisplayItem{
|
||||
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.in_reply_to, account.displayName), account.emojis, R.drawable.ic_reply_20px));
|
||||
}
|
||||
HeaderStatusDisplayItem header;
|
||||
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null));
|
||||
if((flags & FLAG_CHECKABLE)!=0)
|
||||
items.add(header=new CheckableHeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null));
|
||||
else
|
||||
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null));
|
||||
|
||||
ArrayList<StatusDisplayItem> contentItems;
|
||||
if(!TextUtils.isEmpty(statusForContent.spoilerText)){
|
||||
@@ -99,7 +117,10 @@ public abstract class StatusDisplayItem{
|
||||
List<Attachment> imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList());
|
||||
if(!imageAttachments.isEmpty()){
|
||||
PhotoLayoutHelper.TiledLayoutResult layout=PhotoLayoutHelper.processThumbs(imageAttachments);
|
||||
contentItems.add(new MediaGridStatusDisplayItem(parentID, fragment, layout, imageAttachments, statusForContent));
|
||||
MediaGridStatusDisplayItem mediaGrid=new MediaGridStatusDisplayItem(parentID, fragment, layout, imageAttachments, statusForContent);
|
||||
if((flags & FLAG_MEDIA_FORCE_HIDDEN)!=0)
|
||||
mediaGrid.sensitiveTitle=fragment.getString(R.string.media_hidden);
|
||||
contentItems.add(mediaGrid);
|
||||
}
|
||||
for(Attachment att:statusForContent.mediaAttachments){
|
||||
if(att.type==Attachment.Type.AUDIO){
|
||||
@@ -112,12 +133,13 @@ public abstract class StatusDisplayItem{
|
||||
if(statusForContent.card!=null && statusForContent.mediaAttachments.isEmpty() && TextUtils.isEmpty(statusForContent.spoilerText)){
|
||||
contentItems.add(new LinkCardStatusDisplayItem(parentID, fragment, statusForContent));
|
||||
}
|
||||
if(addFooter){
|
||||
if((flags & FLAG_NO_FOOTER)==0){
|
||||
items.add(new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID));
|
||||
if(status.hasGapAfter && !(fragment instanceof ThreadFragment))
|
||||
items.add(new GapStatusDisplayItem(parentID, fragment));
|
||||
}
|
||||
int i=1;
|
||||
boolean inset=(flags & FLAG_INSET)!=0;
|
||||
for(StatusDisplayItem item:items){
|
||||
item.inset=inset;
|
||||
item.index=i++;
|
||||
@@ -156,7 +178,8 @@ public abstract class StatusDisplayItem{
|
||||
EXTENDED_FOOTER,
|
||||
MEDIA_GRID,
|
||||
SPOILER,
|
||||
SECTION_HEADER
|
||||
SECTION_HEADER,
|
||||
HEADER_CHECKABLE
|
||||
}
|
||||
|
||||
public static abstract class Holder<T extends StatusDisplayItem> extends BindableViewHolder<T> implements UsableRecyclerView.DisableableClickable{
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.Button;
|
||||
import android.widget.PopupMenu;
|
||||
@@ -740,4 +741,13 @@ public class UiUtils{
|
||||
ta.recycle();
|
||||
return d;
|
||||
}
|
||||
|
||||
public static WindowInsets applyBottomInsetToFixedView(View view, WindowInsets insets){
|
||||
if(Build.VERSION.SDK_INT>=27){
|
||||
int inset=insets.getSystemWindowInsetBottom();
|
||||
view.setPadding(0, 0, 0, inset>0 ? Math.max(inset, V.dp(40)) : 0);
|
||||
return insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0);
|
||||
}
|
||||
return insets;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +228,7 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putParcelable("reportAccount", Parcels.wrap(account));
|
||||
args.putParcelable("relationship", Parcels.wrap(relationship));
|
||||
Nav.go(fragment.getActivity(), ReportReasonChoiceFragment.class, args);
|
||||
}else if(id==R.id.open_in_browser){
|
||||
UiUtils.launchWebBrowser(fragment.getActivity(), account.url);
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.joinmastodon.android.ui.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.Checkable;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
@@ -47,4 +48,11 @@ public class CheckableLinearLayout extends LinearLayout implements Checkable{
|
||||
}
|
||||
return drawableState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info){
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
info.setCheckable(true);
|
||||
info.setChecked(checked);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.joinmastodon.android.ui.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.Checkable;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
public class CheckableRelativeLayout extends RelativeLayout implements Checkable{
|
||||
private boolean checked;
|
||||
private static final int[] CHECKED_STATE_SET = {
|
||||
android.R.attr.state_checked
|
||||
};
|
||||
|
||||
public CheckableRelativeLayout(Context context){
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public CheckableRelativeLayout(Context context, AttributeSet attrs){
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public CheckableRelativeLayout(Context context, AttributeSet attrs, int defStyle){
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(boolean checked){
|
||||
this.checked=checked;
|
||||
refreshDrawableState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked(){
|
||||
return checked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggle(){
|
||||
setChecked(!checked);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int[] onCreateDrawableState(int extraSpace) {
|
||||
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
|
||||
if (isChecked()) {
|
||||
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
|
||||
}
|
||||
return drawableState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info){
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
info.setCheckable(true);
|
||||
info.setChecked(checked);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.joinmastodon.android.ui.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Switch;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class M3Switch extends Switch{
|
||||
private boolean ignoreRequestLayout;
|
||||
private DummyDrawable dummyDrawable=new DummyDrawable();
|
||||
|
||||
public M3Switch(Context context){
|
||||
super(context);
|
||||
}
|
||||
|
||||
public M3Switch(Context context, AttributeSet attrs){
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public M3Switch(Context context, AttributeSet attrs, int defStyleAttr){
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
|
||||
ignoreRequestLayout=true;
|
||||
Drawable prevThumbDrawable=getThumbDrawable();
|
||||
setThumbDrawable(dummyDrawable);
|
||||
ignoreRequestLayout=false;
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
ignoreRequestLayout=true;
|
||||
setThumbDrawable(prevThumbDrawable);
|
||||
ignoreRequestLayout=false;
|
||||
try{
|
||||
Field fld=Switch.class.getDeclaredField("mThumbWidth");
|
||||
fld.setAccessible(true);
|
||||
fld.set(this, V.dp(32));
|
||||
}catch(Exception ignore){}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestLayout(){
|
||||
if(ignoreRequestLayout)
|
||||
return;
|
||||
super.requestLayout();
|
||||
}
|
||||
|
||||
private static class DummyDrawable extends Drawable{
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(@Nullable ColorFilter colorFilter){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth(){
|
||||
return V.dp(26);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user