refactor: start of refactor of previewless media status display item

Its crashing a lot, and has a lot to be done
This commit is contained in:
LucasGGamerM 2023-09-16 16:14:48 -03:00
parent ed6aa0725e
commit 31a7aa9d40
5 changed files with 422 additions and 3 deletions

View File

@ -44,6 +44,7 @@ import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.PollFooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.PollOptionStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.PreviewlessMediaGridStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.SpoilerStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
@ -51,6 +52,7 @@ import org.joinmastodon.android.ui.displayitems.WarningFilteredStatusDisplayItem
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
import org.joinmastodon.android.ui.utils.MediaAttachmentViewController;
import org.joinmastodon.android.ui.utils.PreviewlessMediaAttachmentViewController;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.utils.ProvidesAssistContent;
import org.joinmastodon.android.utils.TypedObjectPool;
@ -90,6 +92,8 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
protected HashMap<String, Relationship> relationships=new HashMap<>();
protected Rect tmpRect=new Rect();
protected TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, MediaAttachmentViewController> attachmentViewsPool=new TypedObjectPool<>(this::makeNewMediaAttachmentView);
protected TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, PreviewlessMediaAttachmentViewController> previewlessAttachmentViewsPool=new TypedObjectPool<>(this::makeNewPreviewlessMediaAttachmentView);
protected boolean currentlyScrolling;
public BaseStatusListFragment(){
@ -280,6 +284,79 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
});
}
public void openPreviewlessMediaPhotoViewer(String parentID, Status _status, int attachmentIndex, PreviewlessMediaGridStatusDisplayItem.Holder gridHolder){
final Status status=_status.getContentStatus();
currentPhotoViewer=new PhotoViewer(getActivity(), status.mediaAttachments, attachmentIndex, new PhotoViewer.Listener(){
private PreviewlessMediaAttachmentViewController transitioningHolder;
@Override
public void setPhotoViewVisibility(int index, boolean visible){
}
@Override
public boolean startPhotoViewTransition(int index, @NonNull Rect outRect, @NonNull int[] outCornerRadius){
PreviewlessMediaAttachmentViewController holder=findPhotoViewHolder(index);
if(holder!=null && list!=null){
transitioningHolder=holder;
View view=transitioningHolder.inner;
int[] pos={0, 0};
view.getLocationOnScreen(pos);
outRect.set(pos[0], pos[1], pos[0]+view.getWidth(), pos[1]+view.getHeight());
list.setClipChildren(false);
gridHolder.setClipChildren(false);
transitioningHolder.view.setElevation(1f);
return true;
}
return false;
}
@Override
public void setTransitioningViewTransform(float translateX, float translateY, float scale){
View view=transitioningHolder.inner;
view.setTranslationX(translateX);
view.setTranslationY(translateY);
view.setScaleX(scale);
view.setScaleY(scale);
}
@Override
public void endPhotoViewTransition(){
View view=transitioningHolder.inner;
view.setTranslationX(0f);
view.setTranslationY(0f);
view.setScaleX(1f);
view.setScaleY(1f);
transitioningHolder.view.setElevation(0f);
if(list!=null)
list.setClipChildren(true);
gridHolder.setClipChildren(true);
transitioningHolder=null;
}
@Nullable
@Override
public Drawable getPhotoViewCurrentDrawable(int index){
return null;
}
@Override
public void photoViewerDismissed(){
currentPhotoViewer=null;
}
@Override
public void onRequestPermissions(String[] permissions){
requestPermissions(permissions, PhotoViewer.PERMISSION_REQUEST);
}
private PreviewlessMediaAttachmentViewController findPhotoViewHolder(int index){
return gridHolder.getViewController(index);
}
});
}
@Override
public @Nullable View getFab() {
if (getParentFragment() instanceof HasFab l) return l.getFab();
@ -768,10 +845,18 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
return new MediaAttachmentViewController(getActivity(), type);
}
private PreviewlessMediaAttachmentViewController makeNewPreviewlessMediaAttachmentView(MediaGridStatusDisplayItem.GridItemType type){
return new PreviewlessMediaAttachmentViewController(getActivity(), type);
}
public TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, MediaAttachmentViewController> getAttachmentViewsPool(){
return attachmentViewsPool;
}
public TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, PreviewlessMediaAttachmentViewController> getPreviewlessAttachmentViewsPool(){
return previewlessAttachmentViewsPool;
}
@Override
public void onProvideAssistContent(AssistContent assistContent) {
assistContent.setWebUri(getWebUri(getSession().getInstanceUri().buildUpon()));

View File

@ -0,0 +1,176 @@
package org.joinmastodon.android.ui.displayitems;
import static org.joinmastodon.android.GlobalUserPreferences.*;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.text.Layout;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.PhotoLayoutHelper;
import org.joinmastodon.android.ui.drawables.SpoilerStripesDrawable;
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
import org.joinmastodon.android.ui.utils.MediaAttachmentViewController;
import org.joinmastodon.android.ui.utils.PreviewlessMediaAttachmentViewController;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.FrameLayoutThatOnlyMeasuresFirstChild;
import org.joinmastodon.android.ui.views.MaxWidthFrameLayout;
import org.joinmastodon.android.ui.views.MediaGridLayout;
import org.joinmastodon.android.ui.views.PreviewlessMediaGridLayout;
import org.joinmastodon.android.utils.TypedObjectPool;
import java.util.ArrayList;
import java.util.List;
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.CubicBezierInterpolator;
import me.grishka.appkit.utils.V;
public class PreviewlessMediaGridStatusDisplayItem extends StatusDisplayItem{
private static final String TAG="PreviewlessMediaGridDisplayItem";
private PhotoLayoutHelper.TiledLayoutResult tiledLayout;
private final TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, PreviewlessMediaAttachmentViewController> viewPool;
private final List<Attachment> attachments;
private final ArrayList<ImageLoaderRequest> requests=new ArrayList<>();
public final Status status;
public String sensitiveTitle;
public PreviewlessMediaGridStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, PhotoLayoutHelper.TiledLayoutResult tiledLayout, List<Attachment> attachments, Status status){
super(parentID, parentFragment);
this.tiledLayout=tiledLayout;
this.viewPool=parentFragment.getPreviewlessAttachmentViewsPool();
this.attachments=attachments;
this.status=status;
// for(Attachment att:attachments){
// requests.add(new UrlImageLoaderRequest(switch(att.type){
// case IMAGE -> att.url;
// case VIDEO, GIFV -> att.previewUrl == null ? att.url : att.previewUrl;
// default -> throw new IllegalStateException("Unexpected value: "+att.url);
// }, 1000, 1000));
// }
}
@Override
public Type getType(){
return Type.PREVIEWLESS_MEDIA_GRID;
}
@Override
public int getImageCount(){
return attachments.size();
}
@Override
public ImageLoaderRequest getImageRequest(int index){
return requests.get(index);
}
public static class Holder extends StatusDisplayItem.Holder<PreviewlessMediaGridStatusDisplayItem> {
private final FrameLayout wrapper;
private final LinearLayout layout;
private final View.OnClickListener clickListener=this::onViewClick;
private final ArrayList<PreviewlessMediaAttachmentViewController> controllers=new ArrayList<>();
// private final FrameLayout hideSensitiveButton;
public Holder(Activity activity, ViewGroup parent){
super(new FrameLayoutThatOnlyMeasuresFirstChild(activity));
wrapper=(FrameLayout)itemView;
layout= new LinearLayout(activity);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layout.setLayoutParams(params);
wrapper.addView(layout);
wrapper.setClipToPadding(false);
// megalodon: no sensitive hide button because the visibility toggle looks prettier imo
// hideSensitiveButton=(FrameLayout) activity.getLayoutInflater().inflate(R.layout.alt_text_badge, overlays, false);
// ((TextView) hideSensitiveButton.findViewById(R.id.alt_button)).setText(R.string.hide);
// overlays.addView(hideSensitiveButton, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.END | Gravity.TOP));
// hideSensitiveButton.setOnClickListener(v->hideSensitive());
}
@Override
public void onBind(PreviewlessMediaGridStatusDisplayItem item){
wrapper.setPadding(0, 0, 0, 0); // item.inset ? 0 : V.dp(8));
// if(altTextAnimator!=null)
// altTextAnimator.cancel();
for(PreviewlessMediaAttachmentViewController c:controllers){
item.viewPool.reuse(c.type, c);
}
layout.removeAllViews();
controllers.clear();
int i=0;
// if (!item.attachments.isEmpty()) updateBlurhashInSensitiveOverlay();
for(Attachment att:item.attachments){
PreviewlessMediaAttachmentViewController c=item.viewPool.obtain(switch(att.type){
case IMAGE -> MediaGridStatusDisplayItem.GridItemType.PHOTO;
case VIDEO -> MediaGridStatusDisplayItem.GridItemType.VIDEO;
case GIFV -> MediaGridStatusDisplayItem.GridItemType.GIFV;
default -> throw new IllegalStateException("Unexpected value: "+att.type);
});
if(c.view.getLayoutParams()==null)
c.view.setLayoutParams(new PreviewlessMediaGridLayout.LayoutParams(item.tiledLayout.tiles[i]));
else
((MediaGridLayout.LayoutParams) c.view.getLayoutParams()).tile=item.tiledLayout.tiles[i];
layout.addView(c.view);
c.view.setOnClickListener(clickListener);
c.view.setTag(i);
controllers.add(c);
c.bind(att, item.status);
i++;
}
boolean insetAndLast=item.inset && isLastDisplayItemForStatus();
wrapper.setClipToOutline(insetAndLast);
wrapper.setOutlineProvider(insetAndLast ? OutlineProviders.bottomRoundedRect(12) : null);
}
private void onViewClick(View v){
int index=(Integer)v.getTag();
item.parentFragment.openPreviewlessMediaPhotoViewer(item.parentID, item.status, index, this);
}
public PreviewlessMediaAttachmentViewController getViewController(int index){
return controllers.get(index);
}
public void setClipChildren(boolean clip){
layout.setClipChildren(clip);
wrapper.setClipChildren(clip);
}
public LinearLayout getLayout(){
return layout;
}
}
}

View File

@ -116,6 +116,7 @@ public abstract class StatusDisplayItem{
case GAP -> new GapStatusDisplayItem.Holder(activity, parent);
case EXTENDED_FOOTER -> new ExtendedFooterStatusDisplayItem.Holder(activity, parent);
case MEDIA_GRID -> new MediaGridStatusDisplayItem.Holder(activity, parent);
case PREVIEWLESS_MEDIA_GRID -> new PreviewlessMediaGridStatusDisplayItem.Holder(activity, parent);
case WARNING -> new WarningFilteredStatusDisplayItem.Holder(activity, parent);
case FILE -> new FileStatusDisplayItem.Holder(activity, parent);
case SPOILER, FILTER_SPOILER -> new SpoilerStatusDisplayItem.Holder(activity, parent, type);
@ -269,9 +270,8 @@ public abstract class StatusDisplayItem{
contentItems.add(mediaGrid);
}
if((flags & FLAG_NO_MEDIA_PREVIEW)!=0){
for(Attachment att:imageAttachments){
contentItems.add(new FileStatusDisplayItem(parentID, fragment, att, statusForContent));
}
contentItems.add(new PreviewlessMediaGridStatusDisplayItem(parentID, fragment, null, imageAttachments, statusForContent));
}
for(Attachment att:statusForContent.mediaAttachments){
if(att.type==Attachment.Type.AUDIO){
@ -362,6 +362,7 @@ public abstract class StatusDisplayItem{
GAP,
EXTENDED_FOOTER,
MEDIA_GRID,
PREVIEWLESS_MEDIA_GRID,
WARNING,
FILE,
SPOILER,

View File

@ -0,0 +1,56 @@
package org.joinmastodon.android.ui.utils;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem;
import org.joinmastodon.android.ui.drawables.BlurhashCrossfadeDrawable;
import org.joinmastodon.android.ui.drawables.PlayIconDrawable;
public class PreviewlessMediaAttachmentViewController{
public final View view;
public final MediaGridStatusDisplayItem.GridItemType type;
private final TextView title, domain;
public final View inner;
private final ImageView icon;
private final Context context;
private Status status;
public PreviewlessMediaAttachmentViewController(Context context, MediaGridStatusDisplayItem.GridItemType type){
view=context.getSystemService(LayoutInflater.class).inflate(R.layout.display_item_file, null);
title=view.findViewById(R.id.title);
domain=view.findViewById(R.id.domain);
icon=view.findViewById(R.id.imageView);
inner=view.findViewById(R.id.inner);
this.context=context;
this.type=type;
}
public void bind(Attachment attachment, Status status){
this.status=status;
title.setText(attachment.description != null
? attachment.description
: context.getString(R.string.sk_no_alt_text));
title.setSingleLine(false);
domain.setText(status.sensitive ? context.getString(R.string.sensitive_content_explain) : null);
domain.setVisibility(status.sensitive ? View.VISIBLE : View.GONE);
if(attachment.type == Attachment.Type.IMAGE)
icon.setImageDrawable(context.getDrawable(R.drawable.ic_fluent_image_24_regular));
if(attachment.type == Attachment.Type.VIDEO)
icon.setImageDrawable(context.getDrawable(R.drawable.ic_fluent_video_clip_24_regular));
if(attachment.type == Attachment.Type.GIFV)
icon.setImageDrawable(context.getDrawable(R.drawable.ic_fluent_gif_24_regular));
}
}

View File

@ -0,0 +1,101 @@
package org.joinmastodon.android.ui.views;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import org.joinmastodon.android.ui.PhotoLayoutHelper;
import org.joinmastodon.android.ui.utils.UiUtils;
import me.grishka.appkit.utils.V;
public class PreviewlessMediaGridLayout extends LinearLayout{
private static final String TAG="PreviewlessMediaGridLayout";
private static final int GAP=2; // dp
private PhotoLayoutHelper.TiledLayoutResult tiledLayout;
public PreviewlessMediaGridLayout(Context context){
this(context, null);
}
public PreviewlessMediaGridLayout(Context context, AttributeSet attrs){
this(context, attrs, 0);
}
public PreviewlessMediaGridLayout(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
// if(tiledLayout==null){
// setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), 0);
// return;
// }
// int width=Math.min(UiUtils.MAX_WIDTH, MeasureSpec.getSize(widthMeasureSpec));
// int height=Math.round(width*(tiledLayout.height/(float)PhotoLayoutHelper.MAX_WIDTH));
// if(tiledLayout.width<PhotoLayoutHelper.MAX_WIDTH){
// width=Math.round(width*(tiledLayout.width/(float)PhotoLayoutHelper.MAX_WIDTH));
// }
//
// for(int i=0;i<tiledLayout.rowSizes.length;i++){
// rowStarts[i]=offset;
// offset+=Math.round(tiledLayout.rowSizes[i]/(float)tiledLayout.height*height);
// rowEnds[i]=offset;
// offset+=V.dp(GAP);
// }
// rowEnds[tiledLayout.rowSizes.length-1]=height;
//
// for(int i=0;i<getChildCount();i++){
// View child=getChildAt(i);
// LayoutParams lp=(LayoutParams) child.getLayoutParams();
// int colSpan=Math.max(1, lp.tile.colSpan)-1;
// int rowSpan=Math.max(1, lp.tile.rowSpan)-1;
// int w=columnEnds[lp.tile.startCol+colSpan]-columnStarts[lp.tile.startCol];
// int h=rowEnds[lp.tile.startRow+rowSpan]-rowStarts[lp.tile.startRow];
// child.measure(w | MeasureSpec.EXACTLY, h | MeasureSpec.EXACTLY);
// }
//
// setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b){
if(tiledLayout==null)
return;
int maxWidth=UiUtils.MAX_WIDTH;
if(tiledLayout.width<PhotoLayoutHelper.MAX_WIDTH){
maxWidth=Math.round((r-l)*(tiledLayout.width/(float)PhotoLayoutHelper.MAX_WIDTH));
}
int xOffset=0;
if(r-l>maxWidth){
xOffset=(r-l)/2-maxWidth/2;
}
for(int i=0;i<getChildCount();i++){
View child=getChildAt(i);
LayoutParams lp=(LayoutParams) child.getLayoutParams();
int colSpan=Math.max(1, lp.tile.colSpan)-1;
int rowSpan=Math.max(1, lp.tile.rowSpan)-1;
// child.layout(columnStarts[lp.tile.startCol]+xOffset, rowStarts[lp.tile.startRow], columnEnds[lp.tile.startCol+colSpan]+xOffset, rowEnds[lp.tile.startRow+rowSpan]);
}
}
public void setTiledLayout(PhotoLayoutHelper.TiledLayoutResult tiledLayout){
this.tiledLayout=tiledLayout;
requestLayout();
}
public static class LayoutParams extends ViewGroup.LayoutParams{
public PhotoLayoutHelper.TiledLayoutResult.Tile tile;
public LayoutParams(PhotoLayoutHelper.TiledLayoutResult.Tile tile){
super(WRAP_CONTENT, WRAP_CONTENT);
this.tile=tile;
}
}
}