Media layout
This commit is contained in:
parent
bb463aa10a
commit
6cc6fe195b
|
@ -22,6 +22,8 @@ import org.joinmastodon.android.model.DisplayItemsParent;
|
||||||
import org.joinmastodon.android.model.Poll;
|
import org.joinmastodon.android.model.Poll;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||||
|
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||||
|
import org.joinmastodon.android.ui.TileGridLayoutManager;
|
||||||
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
|
||||||
|
@ -32,6 +34,7 @@ import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
||||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
|
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -193,6 +196,11 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endPhotoViewTransition(){
|
public void endPhotoViewTransition(){
|
||||||
|
// fix drawable callback
|
||||||
|
Drawable d=transitioningHolder.photo.getDrawable();
|
||||||
|
transitioningHolder.photo.setImageDrawable(null);
|
||||||
|
transitioningHolder.photo.setImageDrawable(d);
|
||||||
|
|
||||||
View view=transitioningHolder.photo;
|
View view=transitioningHolder.photo;
|
||||||
view.setTranslationX(0f);
|
view.setTranslationX(0f);
|
||||||
view.setTranslationY(0f);
|
view.setTranslationY(0f);
|
||||||
|
@ -265,6 +273,46 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
|
||||||
|
RecyclerView.ViewHolder holder=parent.getChildViewHolder(view);
|
||||||
|
if(holder instanceof ImageStatusDisplayItem.Holder){
|
||||||
|
int width=Math.min(parent.getWidth(), V.dp(ImageAttachmentFrameLayout.MAX_WIDTH));
|
||||||
|
PhotoLayoutHelper.TiledLayoutResult layout=((ImageStatusDisplayItem.Holder<?>) holder).getItem().tiledLayout;
|
||||||
|
PhotoLayoutHelper.TiledLayoutResult.Tile tile=((ImageStatusDisplayItem.Holder<?>) holder).getItem().thisTile;
|
||||||
|
if(tile.startCol+tile.colSpan<layout.columnSizes.length){
|
||||||
|
outRect.right=V.dp(1);
|
||||||
|
}
|
||||||
|
if(tile.startRow+tile.rowSpan<layout.rowSizes.length){
|
||||||
|
outRect.bottom=V.dp(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For a view that spans rows, compensate its additional height so the row it's in stays the right height
|
||||||
|
if(tile.rowSpan>1){
|
||||||
|
outRect.bottom=-(Math.round(tile.height/1000f*width)-Math.round(layout.rowSizes[tile.startRow]/1000f*width));
|
||||||
|
}
|
||||||
|
// ...and for its siblings, offset those on rows below first to the right where they belong
|
||||||
|
if(tile.startCol>0 && layout.tiles[0].rowSpan>1 && tile.startRow>layout.tiles[0].startRow){
|
||||||
|
int xOffset=Math.round(layout.tiles[0].width/1000f*parent.getWidth());
|
||||||
|
outRect.left=xOffset;
|
||||||
|
outRect.right=-xOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the width of the media block is smaller than that of the RecyclerView, offset the views horizontally to center them
|
||||||
|
if(parent.getWidth()>width){
|
||||||
|
outRect.left+=(parent.getWidth()-V.dp(ImageAttachmentFrameLayout.MAX_WIDTH))/2;
|
||||||
|
if(tile.startCol>0){
|
||||||
|
int spanOffset=0;
|
||||||
|
for(int i=0;i<tile.startCol;i++){
|
||||||
|
spanOffset+=layout.columnSizes[i];
|
||||||
|
}
|
||||||
|
outRect.left-=Math.round(spanOffset/1000f*parent.getWidth());
|
||||||
|
outRect.left+=Math.round(spanOffset/1000f*width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
((UsableRecyclerView)list).setSelectorBoundsProvider(new UsableRecyclerView.SelectorBoundsProvider(){
|
((UsableRecyclerView)list).setSelectorBoundsProvider(new UsableRecyclerView.SelectorBoundsProvider(){
|
||||||
@Override
|
@Override
|
||||||
|
@ -295,7 +343,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RecyclerView.LayoutManager onCreateLayoutManager(){
|
protected RecyclerView.LayoutManager onCreateLayoutManager(){
|
||||||
GridLayoutManager lm=new GridLayoutManager(getActivity(), 2);
|
GridLayoutManager lm=new TileGridLayoutManager(getActivity(), 1000);
|
||||||
lm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup(){
|
lm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup(){
|
||||||
@Override
|
@Override
|
||||||
public int getSpanSize(int position){
|
public int getSpanSize(int position){
|
||||||
|
@ -303,14 +351,16 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
if(position>=0 && position<displayItems.size()){
|
if(position>=0 && position<displayItems.size()){
|
||||||
StatusDisplayItem item=displayItems.get(position);
|
StatusDisplayItem item=displayItems.get(position);
|
||||||
if(item instanceof ImageStatusDisplayItem){
|
if(item instanceof ImageStatusDisplayItem){
|
||||||
int total=((ImageStatusDisplayItem) item).totalPhotos;
|
PhotoLayoutHelper.TiledLayoutResult layout=((ImageStatusDisplayItem) item).tiledLayout;
|
||||||
if(total>1){
|
PhotoLayoutHelper.TiledLayoutResult.Tile tile=((ImageStatusDisplayItem) item).thisTile;
|
||||||
int index=((ImageStatusDisplayItem) item).index;
|
int spans=0;
|
||||||
return 1;
|
for(int i=0;i<tile.colSpan;i++){
|
||||||
|
spans+=layout.columnSizes[tile.startCol+i];
|
||||||
}
|
}
|
||||||
|
return spans;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 2;
|
return 1000;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return lm;
|
return lm;
|
||||||
|
@ -320,6 +370,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
public void onConfigurationChanged(Configuration newConfig){
|
public void onConfigurationChanged(Configuration newConfig){
|
||||||
super.onConfigurationChanged(newConfig);
|
super.onConfigurationChanged(newConfig);
|
||||||
updateToolbar();
|
updateToolbar();
|
||||||
|
list.invalidateItemDecorations();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateToolbar(){
|
private void updateToolbar(){
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.notifications.GetNotifications;
|
import org.joinmastodon.android.api.requests.notifications.GetNotifications;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.model.Poll;
|
import org.joinmastodon.android.model.Poll;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
public class NotificationsFragment extends BaseStatusListFragment<Notification>{
|
public class NotificationsFragment extends BaseStatusListFragment<Notification>{
|
||||||
|
@ -71,7 +75,16 @@ public class NotificationsFragment extends BaseStatusListFragment<Notification>{
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(String id){
|
public void onItemClick(String id){
|
||||||
|
Notification n=getNotificationByID(id);
|
||||||
|
if(n.status!=null){
|
||||||
|
Status status=n.status;
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putParcelable("status", Parcels.wrap(status));
|
||||||
|
if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId))
|
||||||
|
args.putParcelable("inReplyToAccount", Parcels.wrap(knownAccounts.get(status.inReplyToAccountId)));
|
||||||
|
Nav.go(getActivity(), ThreadFragment.class, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -147,6 +147,8 @@ public class Account extends BaseModel{
|
||||||
}
|
}
|
||||||
if(moved!=null)
|
if(moved!=null)
|
||||||
moved.postprocess();
|
moved.postprocess();
|
||||||
|
if(TextUtils.isEmpty(displayName))
|
||||||
|
displayName=username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLocal(){
|
public boolean isLocal(){
|
||||||
|
|
|
@ -11,6 +11,8 @@ import org.joinmastodon.android.api.RequiredField;
|
||||||
import org.joinmastodon.android.ui.utils.BlurHashDecoder;
|
import org.joinmastodon.android.ui.utils.BlurHashDecoder;
|
||||||
import org.joinmastodon.android.ui.utils.BlurHashDrawable;
|
import org.joinmastodon.android.ui.utils.BlurHashDrawable;
|
||||||
import org.parceler.Parcel;
|
import org.parceler.Parcel;
|
||||||
|
import org.parceler.ParcelConstructor;
|
||||||
|
import org.parceler.ParcelProperty;
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
public class Attachment extends BaseModel{
|
public class Attachment extends BaseModel{
|
||||||
|
@ -23,11 +25,24 @@ public class Attachment extends BaseModel{
|
||||||
public String previewUrl;
|
public String previewUrl;
|
||||||
public String remoteUrl;
|
public String remoteUrl;
|
||||||
public String description;
|
public String description;
|
||||||
|
@ParcelProperty("blurhash")
|
||||||
public String blurhash;
|
public String blurhash;
|
||||||
public Metadata meta;
|
public Metadata meta;
|
||||||
|
|
||||||
public transient Drawable blurhashPlaceholder;
|
public transient Drawable blurhashPlaceholder;
|
||||||
|
|
||||||
|
public Attachment(){}
|
||||||
|
|
||||||
|
@ParcelConstructor
|
||||||
|
public Attachment(@ParcelProperty("blurhash") String blurhash){
|
||||||
|
this.blurhash=blurhash;
|
||||||
|
if(blurhash!=null){
|
||||||
|
Bitmap placeholder=BlurHashDecoder.decode(blurhash, 16, 16);
|
||||||
|
if(placeholder!=null)
|
||||||
|
blurhashPlaceholder=new BlurHashDrawable(placeholder, getWidth(), getHeight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int getWidth(){
|
public int getWidth(){
|
||||||
if(meta==null)
|
if(meta==null)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -86,7 +101,11 @@ public class Attachment extends BaseModel{
|
||||||
@SerializedName("audio")
|
@SerializedName("audio")
|
||||||
AUDIO,
|
AUDIO,
|
||||||
@SerializedName("unknown")
|
@SerializedName("unknown")
|
||||||
UNKNOWN
|
UNKNOWN;
|
||||||
|
|
||||||
|
public boolean isImage(){
|
||||||
|
return this==IMAGE || this==GIFV || this==VIDEO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
|
|
|
@ -0,0 +1,338 @@
|
||||||
|
package org.joinmastodon.android.ui;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.model.Attachment;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class PhotoLayoutHelper{
|
||||||
|
@NonNull
|
||||||
|
public static TiledLayoutResult processThumbs(int _maxW, int _maxH, List<Attachment> thumbs){
|
||||||
|
TiledLayoutResult result=new TiledLayoutResult();
|
||||||
|
if(thumbs.size()==1){
|
||||||
|
Attachment att=thumbs.get(0);
|
||||||
|
result.rowSizes=result.columnSizes=new int[]{1};
|
||||||
|
if(att.getWidth()>att.getHeight()){
|
||||||
|
result.width=_maxW;
|
||||||
|
result.height=Math.round(att.getHeight()/(float)att.getWidth()*_maxW);
|
||||||
|
}else{
|
||||||
|
result.height=_maxH;
|
||||||
|
result.width=Math.round(att.getWidth()/(float)att.getHeight()*_maxH);
|
||||||
|
}
|
||||||
|
result.tiles=new TiledLayoutResult.Tile[]{new TiledLayoutResult.Tile(1, 1, result.width, result.height, 0, 0)};
|
||||||
|
}else if(thumbs.size()==0){
|
||||||
|
throw new IllegalArgumentException("Empty thumbs array");
|
||||||
|
}
|
||||||
|
|
||||||
|
String orients="";
|
||||||
|
ArrayList<Float> ratios=new ArrayList<Float>();
|
||||||
|
int cnt=thumbs.size();
|
||||||
|
|
||||||
|
|
||||||
|
for(Attachment thumb : thumbs){
|
||||||
|
// float ratio=thumb.isSizeKnown() ? thumb.getWidth()/(float) thumb.getHeight() : 1f;
|
||||||
|
float ratio=thumb.getWidth()/(float) thumb.getHeight();
|
||||||
|
char orient=ratio>1.2 ? 'w' : (ratio<0.8 ? 'n' : 'q');
|
||||||
|
orients+=orient;
|
||||||
|
ratios.add(ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
float avgRatio=!ratios.isEmpty() ? sum(ratios)/ratios.size() : 1.0f;
|
||||||
|
|
||||||
|
float maxW, maxH, marginW=0, marginH=0;
|
||||||
|
if(_maxW>0){
|
||||||
|
maxW=_maxW;
|
||||||
|
maxH=_maxH;
|
||||||
|
}else{
|
||||||
|
maxW=510;
|
||||||
|
maxH=510;
|
||||||
|
}
|
||||||
|
|
||||||
|
float maxRatio=maxW/maxH;
|
||||||
|
|
||||||
|
if(cnt==2){
|
||||||
|
if(orients.equals("ww") && avgRatio>1.4*maxRatio && (ratios.get(1)-ratios.get(0))<0.2){ // two wide photos, one above the other
|
||||||
|
float h=Math.min(maxW/ratios.get(0), Math.min(maxW/ratios.get(1), (maxH-marginH)/2.0f));
|
||||||
|
|
||||||
|
result.width=Math.round(maxW);
|
||||||
|
result.height=Math.round(h*2+marginH);
|
||||||
|
result.columnSizes=new int[]{result.width};
|
||||||
|
result.rowSizes=new int[]{Math.round(h), Math.round(h)};
|
||||||
|
result.tiles=new TiledLayoutResult.Tile[]{
|
||||||
|
new TiledLayoutResult.Tile(1, 1, maxW, h, 0, 0),
|
||||||
|
new TiledLayoutResult.Tile(1, 1, maxW, h, 0, 1)
|
||||||
|
};
|
||||||
|
}else if(orients.equals("ww") || orients.equals("qq")){ // next to each other, same ratio
|
||||||
|
float w=((maxW-marginW)/2);
|
||||||
|
float h=Math.min(w/ratios.get(0), Math.min(w/ratios.get(1), maxH));
|
||||||
|
|
||||||
|
result.width=Math.round(maxW);
|
||||||
|
result.height=Math.round(h);
|
||||||
|
result.columnSizes=new int[]{Math.round(w), _maxW-Math.round(w)};
|
||||||
|
result.rowSizes=new int[]{Math.round(h)};
|
||||||
|
result.tiles=new TiledLayoutResult.Tile[]{
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w, h, 0, 0),
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w, h, 1, 0)
|
||||||
|
};
|
||||||
|
}else{ // next to each other, different ratios
|
||||||
|
float w0=((maxW-marginW)/ratios.get(1)/(1/ratios.get(0)+1/ratios.get(1)));
|
||||||
|
float w1=(maxW-w0-marginW);
|
||||||
|
float h=Math.min(maxH, Math.min(w0/ratios.get(0), w1/ratios.get(1)));
|
||||||
|
|
||||||
|
result.columnSizes=new int[]{Math.round(w0), Math.round(w1)};
|
||||||
|
result.rowSizes=new int[]{Math.round(h)};
|
||||||
|
result.width=Math.round(w0+w1+marginW);
|
||||||
|
result.height=Math.round(h);
|
||||||
|
result.tiles=new TiledLayoutResult.Tile[]{
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w0, h, 0, 0),
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w1, h, 1, 0)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}else if(cnt==3){
|
||||||
|
if(/*(ratios.get(0) > 1.2 * maxRatio || avgRatio > 1.5 * maxRatio) &&*/ orients.equals("www")){ // 2nd and 3rd photos are on the next line
|
||||||
|
float hCover=Math.min(maxW/ratios.get(0), (maxH-marginH)*0.66f);
|
||||||
|
float w2=((maxW-marginW)/2);
|
||||||
|
float h=Math.min(maxH-hCover-marginH, Math.min(w2/ratios.get(1), w2/ratios.get(2)));
|
||||||
|
result.width=Math.round(maxW);
|
||||||
|
result.height=Math.round(hCover+h+marginH);
|
||||||
|
result.columnSizes=new int[]{Math.round(w2), _maxW-Math.round(w2)};
|
||||||
|
result.rowSizes=new int[]{Math.round(hCover), Math.round(h)};
|
||||||
|
result.tiles=new TiledLayoutResult.Tile[]{
|
||||||
|
new TiledLayoutResult.Tile(2, 1, maxW, hCover, 0, 0),
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w2, h, 0, 1),
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w2, h, 1, 1)
|
||||||
|
};
|
||||||
|
}else{ // 2nd and 3rd photos are on the right part
|
||||||
|
float wCover=Math.min(maxH*ratios.get(0), (maxW-marginW)*0.75f);
|
||||||
|
float h1=(ratios.get(1)*(maxH-marginH)/(ratios.get(2)+ratios.get(1)));
|
||||||
|
float h0=(maxH-h1-marginH);
|
||||||
|
float w=Math.min(maxW-wCover-marginW, Math.min(h1*ratios.get(2), h0*ratios.get(1)));
|
||||||
|
result.width=Math.round(wCover+w+marginW);
|
||||||
|
result.height=Math.round(maxH);
|
||||||
|
result.columnSizes=new int[]{Math.round(wCover), Math.round(w)};
|
||||||
|
result.rowSizes=new int[]{Math.round(h0), Math.round(h1)};
|
||||||
|
result.tiles=new TiledLayoutResult.Tile[]{
|
||||||
|
new TiledLayoutResult.Tile(1, 2, wCover, maxH, 0, 0),
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w, h0, 1, 0),
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w, h1, 1, 1)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}else if(cnt==4){
|
||||||
|
if(/*(ratios.get(0) > 1.2 * maxRatio || avgRatio > 1.5 * maxRatio) &&*/ orients.equals("wwww")){ // 2nd, 3rd and 4th photos are on the next line
|
||||||
|
float hCover=Math.min(maxW/ratios.get(0), (maxH-marginH)*0.66f);
|
||||||
|
float h=(maxW-2*marginW)/(ratios.get(1)+ratios.get(2)+ratios.get(3));
|
||||||
|
float w0=h*ratios.get(1);
|
||||||
|
float w1=h*ratios.get(2);
|
||||||
|
float w2=h*ratios.get(3);
|
||||||
|
h=Math.min(maxH-hCover-marginH, h);
|
||||||
|
result.width=Math.round(maxW);
|
||||||
|
result.height=Math.round(hCover+h+marginH);
|
||||||
|
result.columnSizes=new int[]{Math.round(w0), Math.round(w1), _maxW-Math.round(w0)-Math.round(w1)};
|
||||||
|
result.rowSizes=new int[]{Math.round(hCover), Math.round(h)};
|
||||||
|
result.tiles=new TiledLayoutResult.Tile[]{
|
||||||
|
new TiledLayoutResult.Tile(3, 1, maxW, hCover, 0, 0),
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w0, h, 0, 1),
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w1, h, 1, 1),
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w2, h, 2, 1),
|
||||||
|
};
|
||||||
|
}else{ // 2nd, 3rd and 4th photos are on the right part
|
||||||
|
float wCover= Math.min(maxH*ratios.get(0), (maxW-marginW)*0.66f);
|
||||||
|
float w=(maxH-2*marginH)/(1/ratios.get(1)+1/ratios.get(2)+1/ratios.get(3));
|
||||||
|
float h0=w/ratios.get(1);
|
||||||
|
float h1=w/ratios.get(2);
|
||||||
|
float h2=w/ratios.get(3)+marginH;
|
||||||
|
w=Math.min(maxW-wCover-marginW, w);
|
||||||
|
result.width=Math.round(wCover+marginW+w);
|
||||||
|
result.height=Math.round(maxH);
|
||||||
|
result.columnSizes=new int[]{Math.round(wCover), Math.round(w)};
|
||||||
|
result.rowSizes=new int[]{Math.round(h0), Math.round(h1), Math.round(h2)};
|
||||||
|
result.tiles=new TiledLayoutResult.Tile[]{
|
||||||
|
new TiledLayoutResult.Tile(1, 3, wCover, maxH, 0, 0),
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w, h0, 1, 0),
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w, h1, 1, 1),
|
||||||
|
new TiledLayoutResult.Tile(1, 1, w, h2, 1, 2),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
ArrayList<Float> ratiosCropped=new ArrayList<Float>();
|
||||||
|
if(avgRatio>1.1){
|
||||||
|
for(float ratio : ratios){
|
||||||
|
ratiosCropped.add(Math.max(1.0f, ratio));
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
for(float ratio : ratios){
|
||||||
|
ratiosCropped.add(Math.min(1.0f, ratio));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<int[], float[]> tries=new HashMap<>();
|
||||||
|
|
||||||
|
// One line
|
||||||
|
int firstLine, secondLine, thirdLine;
|
||||||
|
tries.put(new int[]{firstLine=cnt}, new float[]{calculateMultiThumbsHeight(ratiosCropped, maxW, marginW)});
|
||||||
|
|
||||||
|
// Two lines
|
||||||
|
for(firstLine=1; firstLine<=cnt-1; firstLine++){
|
||||||
|
tries.put(new int[]{firstLine, secondLine=cnt-firstLine}, new float[]{
|
||||||
|
calculateMultiThumbsHeight(ratiosCropped.subList(0, firstLine), maxW, marginW),
|
||||||
|
calculateMultiThumbsHeight(ratiosCropped.subList(firstLine, ratiosCropped.size()), maxW, marginW)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Three lines
|
||||||
|
for(firstLine=1; firstLine<=cnt-2; firstLine++){
|
||||||
|
for(secondLine=1; secondLine<=cnt-firstLine-1; secondLine++){
|
||||||
|
tries.put(new int[]{firstLine, secondLine, thirdLine=cnt-firstLine-secondLine}, new float[]{
|
||||||
|
calculateMultiThumbsHeight(ratiosCropped.subList(0, firstLine), maxW, marginW),
|
||||||
|
calculateMultiThumbsHeight(ratiosCropped.subList(firstLine, firstLine+secondLine), maxW, marginW),
|
||||||
|
calculateMultiThumbsHeight(ratiosCropped.subList(firstLine+secondLine, ratiosCropped.size()), maxW, marginW)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looking for minimum difference between thumbs block height and maxH (may probably be little over)
|
||||||
|
int[] optConf=null;
|
||||||
|
float optDiff=0;
|
||||||
|
for(int[] conf : tries.keySet()){
|
||||||
|
float[] heights=tries.get(conf);
|
||||||
|
float confH=marginH*(heights.length-1);
|
||||||
|
for(float h : heights) confH+=h;
|
||||||
|
float confDiff=Math.abs(confH-maxH);
|
||||||
|
if(conf.length>1){
|
||||||
|
if(conf[0]>conf[1] || conf.length>2 && conf[1]>conf[2]){
|
||||||
|
confDiff*=1.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(optConf==null || confDiff<optDiff){
|
||||||
|
optConf=conf;
|
||||||
|
optDiff=confDiff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<Attachment> thumbsRemain=new ArrayList<>(thumbs);
|
||||||
|
ArrayList<Float> ratiosRemain=new ArrayList<>(ratiosCropped);
|
||||||
|
float[] optHeights=tries.get(optConf);
|
||||||
|
int k=0;
|
||||||
|
|
||||||
|
result.width=Math.round(maxW);
|
||||||
|
result.rowSizes=new int[optHeights.length];
|
||||||
|
result.tiles=new TiledLayoutResult.Tile[thumbs.size()];
|
||||||
|
float totalHeight=0f;
|
||||||
|
ArrayList<Integer> gridLineOffsets=new ArrayList<>();
|
||||||
|
ArrayList<ArrayList<TiledLayoutResult.Tile>> rowTiles=new ArrayList<>(optHeights.length);
|
||||||
|
|
||||||
|
for(int i=0; i<optConf.length; i++){
|
||||||
|
int lineChunksNum=optConf[i];
|
||||||
|
ArrayList<Attachment> lineThumbs=new ArrayList<>();
|
||||||
|
for(int j=0; j<lineChunksNum; j++) lineThumbs.add(thumbsRemain.remove(0));
|
||||||
|
float lineHeight=optHeights[i];
|
||||||
|
totalHeight+=lineHeight;
|
||||||
|
result.rowSizes[i]=Math.round(lineHeight);
|
||||||
|
int totalWidth=0;
|
||||||
|
ArrayList<TiledLayoutResult.Tile> row=new ArrayList<>();
|
||||||
|
for(int j=0; j<lineThumbs.size(); j++){
|
||||||
|
float thumb_ratio=ratiosRemain.remove(0);
|
||||||
|
float w=j==lineThumbs.size()-1 ? (maxW-totalWidth) : (thumb_ratio*lineHeight);
|
||||||
|
totalWidth+=Math.round(w);
|
||||||
|
if(j<lineThumbs.size()-1 && !gridLineOffsets.contains(totalWidth))
|
||||||
|
gridLineOffsets.add(totalWidth);
|
||||||
|
TiledLayoutResult.Tile tile=new TiledLayoutResult.Tile(1, 1, w, lineHeight, 0, i);
|
||||||
|
result.tiles[k]=tile;
|
||||||
|
row.add(tile);
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
rowTiles.add(row);
|
||||||
|
}
|
||||||
|
Collections.sort(gridLineOffsets);
|
||||||
|
gridLineOffsets.add(Math.round(maxW));
|
||||||
|
result.columnSizes=new int[gridLineOffsets.size()];
|
||||||
|
result.columnSizes[0]=gridLineOffsets.get(0);
|
||||||
|
for(int i=gridLineOffsets.size()-1; i>0; i--){
|
||||||
|
result.columnSizes[i]=gridLineOffsets.get(i)-gridLineOffsets.get(i-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(ArrayList<TiledLayoutResult.Tile> row : rowTiles){
|
||||||
|
int columnOffset=0;
|
||||||
|
for(TiledLayoutResult.Tile tile : row){
|
||||||
|
int startColumn=columnOffset;
|
||||||
|
tile.startCol=startColumn;
|
||||||
|
int width=0;
|
||||||
|
tile.colSpan=0;
|
||||||
|
for(int i=startColumn; i<result.columnSizes.length; i++){
|
||||||
|
width+=result.columnSizes[i];
|
||||||
|
tile.colSpan++;
|
||||||
|
if(width==tile.width){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
columnOffset+=tile.colSpan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.height=Math.round(totalHeight+marginH*(optHeights.length-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float sum(List<Float> a){
|
||||||
|
float sum=0;
|
||||||
|
for(float f:a) sum+=f;
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float calculateMultiThumbsHeight(List<Float> ratios, float width, float margin){
|
||||||
|
return (width-(ratios.size()-1)*margin)/sum(ratios);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class TiledLayoutResult{
|
||||||
|
public int[] columnSizes, rowSizes; // sizes in grid fractions
|
||||||
|
public Tile[] tiles;
|
||||||
|
public int width, height; // in pixels (510x510 max)
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
return "TiledLayoutResult{"+
|
||||||
|
"columnSizes="+Arrays.toString(columnSizes)+
|
||||||
|
", rowSizes="+Arrays.toString(rowSizes)+
|
||||||
|
", tiles="+Arrays.toString(tiles)+
|
||||||
|
", width="+width+
|
||||||
|
", height="+height+
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Tile{
|
||||||
|
public int colSpan, rowSpan, width, height, startCol, startRow;
|
||||||
|
|
||||||
|
public Tile(int colSpan, int rowSpan, int width, int height, int startCol, int startRow){
|
||||||
|
this.colSpan=colSpan;
|
||||||
|
this.rowSpan=rowSpan;
|
||||||
|
this.width=width;
|
||||||
|
this.height=height;
|
||||||
|
this.startCol=startCol;
|
||||||
|
this.startRow=startRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tile(int colSpan, int rowSpan, float width, float height, int startCol, int startRow){
|
||||||
|
this(colSpan, rowSpan, Math.round(width), Math.round(height), startCol, startRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
return "Tile{"+
|
||||||
|
"colSpan="+colSpan+
|
||||||
|
", rowSpan="+rowSpan+
|
||||||
|
", width="+width+
|
||||||
|
", height="+height+
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package org.joinmastodon.android.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
public class TileGridLayoutManager extends GridLayoutManager{
|
||||||
|
private static final String TAG="TileGridLayoutManager";
|
||||||
|
public TileGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TileGridLayoutManager(Context context, int spanCount){
|
||||||
|
super(context, spanCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TileGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout){
|
||||||
|
super(context, spanCount, orientation, reverseLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnCountForAccessibility(RecyclerView.Recycler recycler, RecyclerView.State state){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,12 +10,13 @@ import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||||
|
|
||||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
|
|
||||||
public class GifVStatusDisplayItem extends ImageStatusDisplayItem{
|
public class GifVStatusDisplayItem extends ImageStatusDisplayItem{
|
||||||
public GifVStatusDisplayItem(String parentID, Status status, Attachment attachment, BaseStatusListFragment parentFragment, int index, int totalPhotos){
|
public GifVStatusDisplayItem(String parentID, Status status, Attachment attachment, BaseStatusListFragment parentFragment, int index, int totalPhotos, PhotoLayoutHelper.TiledLayoutResult tiledLayout, PhotoLayoutHelper.TiledLayoutResult.Tile thisTile){
|
||||||
super(parentID, parentFragment, attachment, status, index, totalPhotos);
|
super(parentID, parentFragment, attachment, status, index, totalPhotos, tiledLayout, thisTile);
|
||||||
request=new UrlImageLoaderRequest(attachment.previewUrl, 1000, 1000);
|
request=new UrlImageLoaderRequest(attachment.previewUrl, 1000, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.joinmastodon.android.ui.displayitems;
|
package org.joinmastodon.android.ui.displayitems;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Fragment;
|
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -11,13 +10,14 @@ import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||||
import org.joinmastodon.android.ui.drawables.BlurhashCrossfadeDrawable;
|
import org.joinmastodon.android.ui.drawables.BlurhashCrossfadeDrawable;
|
||||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
|
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
|
||||||
|
import org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout;
|
||||||
|
|
||||||
import androidx.annotation.LayoutRes;
|
import androidx.annotation.LayoutRes;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
|
||||||
|
|
||||||
public abstract class ImageStatusDisplayItem extends StatusDisplayItem{
|
public abstract class ImageStatusDisplayItem extends StatusDisplayItem{
|
||||||
public final int index;
|
public final int index;
|
||||||
|
@ -25,13 +25,17 @@ public abstract class ImageStatusDisplayItem extends StatusDisplayItem{
|
||||||
protected Attachment attachment;
|
protected Attachment attachment;
|
||||||
protected ImageLoaderRequest request;
|
protected ImageLoaderRequest request;
|
||||||
public final Status status;
|
public final Status status;
|
||||||
|
public final PhotoLayoutHelper.TiledLayoutResult tiledLayout;
|
||||||
|
public final PhotoLayoutHelper.TiledLayoutResult.Tile thisTile;
|
||||||
|
|
||||||
public ImageStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Attachment photo, Status status, int index, int totalPhotos){
|
public ImageStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Attachment photo, Status status, int index, int totalPhotos, PhotoLayoutHelper.TiledLayoutResult tiledLayout, PhotoLayoutHelper.TiledLayoutResult.Tile thisTile){
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
this.attachment=photo;
|
this.attachment=photo;
|
||||||
this.status=status;
|
this.status=status;
|
||||||
this.index=index;
|
this.index=index;
|
||||||
this.totalPhotos=totalPhotos;
|
this.totalPhotos=totalPhotos;
|
||||||
|
this.tiledLayout=tiledLayout;
|
||||||
|
this.thisTile=thisTile;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -46,6 +50,7 @@ public abstract class ImageStatusDisplayItem extends StatusDisplayItem{
|
||||||
|
|
||||||
public static abstract class Holder<T extends ImageStatusDisplayItem> extends StatusDisplayItem.Holder<T> implements ImageLoaderViewHolder{
|
public static abstract class Holder<T extends ImageStatusDisplayItem> extends StatusDisplayItem.Holder<T> implements ImageLoaderViewHolder{
|
||||||
public final ImageView photo;
|
public final ImageView photo;
|
||||||
|
private ImageAttachmentFrameLayout layout;
|
||||||
private BlurhashCrossfadeDrawable crossfadeDrawable=new BlurhashCrossfadeDrawable();
|
private BlurhashCrossfadeDrawable crossfadeDrawable=new BlurhashCrossfadeDrawable();
|
||||||
private boolean didClear;
|
private boolean didClear;
|
||||||
|
|
||||||
|
@ -53,10 +58,12 @@ public abstract class ImageStatusDisplayItem extends StatusDisplayItem{
|
||||||
super(activity, layout, parent);
|
super(activity, layout, parent);
|
||||||
photo=findViewById(R.id.photo);
|
photo=findViewById(R.id.photo);
|
||||||
photo.setOnClickListener(this::onViewClick);
|
photo.setOnClickListener(this::onViewClick);
|
||||||
|
this.layout=(ImageAttachmentFrameLayout)itemView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(ImageStatusDisplayItem item){
|
public void onBind(ImageStatusDisplayItem item){
|
||||||
|
layout.setLayout(item.tiledLayout, item.thisTile);
|
||||||
crossfadeDrawable.setSize(item.attachment.getWidth(), item.attachment.getHeight());
|
crossfadeDrawable.setSize(item.attachment.getWidth(), item.attachment.getHeight());
|
||||||
crossfadeDrawable.setBlurhashDrawable(item.attachment.blurhashPlaceholder);
|
crossfadeDrawable.setBlurhashDrawable(item.attachment.blurhashPlaceholder);
|
||||||
crossfadeDrawable.setCrossfadeAlpha(item.status.spoilerRevealed ? 0f : 1f);
|
crossfadeDrawable.setCrossfadeAlpha(item.status.spoilerRevealed ? 0f : 1f);
|
||||||
|
|
|
@ -7,12 +7,13 @@ import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||||
|
|
||||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
|
|
||||||
public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{
|
public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{
|
||||||
public PhotoStatusDisplayItem(String parentID, Status status, Attachment photo, BaseStatusListFragment parentFragment, int index, int totalPhotos){
|
public PhotoStatusDisplayItem(String parentID, Status status, Attachment photo, BaseStatusListFragment parentFragment, int index, int totalPhotos, PhotoLayoutHelper.TiledLayoutResult tiledLayout, PhotoLayoutHelper.TiledLayoutResult.Tile thisTile){
|
||||||
super(parentID, parentFragment, photo, status, index, totalPhotos);
|
super(parentID, parentFragment, photo, status, index, totalPhotos, tiledLayout, thisTile);
|
||||||
request=new UrlImageLoaderRequest(photo.url, 1000, 1000);
|
request=new UrlImageLoaderRequest(photo.url, 1000, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,14 @@ import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.DisplayItemsParent;
|
import org.joinmastodon.android.model.DisplayItemsParent;
|
||||||
import org.joinmastodon.android.model.Poll;
|
import org.joinmastodon.android.model.Poll;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
|
@ -71,22 +73,20 @@ public abstract class StatusDisplayItem{
|
||||||
items.add(new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent));
|
items.add(new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent));
|
||||||
if(!TextUtils.isEmpty(statusForContent.content))
|
if(!TextUtils.isEmpty(statusForContent.content))
|
||||||
items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, accountID), fragment, statusForContent));
|
items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, accountID), fragment, statusForContent));
|
||||||
int photoIndex=0;
|
List<Attachment> imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList());
|
||||||
int totalPhotos=0;
|
if(!imageAttachments.isEmpty()){
|
||||||
for(Attachment attachment:statusForContent.mediaAttachments){
|
int photoIndex=0;
|
||||||
if(attachment.type==Attachment.Type.IMAGE || attachment.type==Attachment.Type.GIFV || attachment.type==Attachment.Type.VIDEO){
|
PhotoLayoutHelper.TiledLayoutResult layout=PhotoLayoutHelper.processThumbs(1000, 1910, imageAttachments);
|
||||||
totalPhotos++;
|
for(Attachment attachment:imageAttachments){
|
||||||
}
|
if(attachment.type==Attachment.Type.IMAGE){
|
||||||
}
|
items.add(new PhotoStatusDisplayItem(parentID, statusForContent, attachment, fragment, photoIndex, imageAttachments.size(), layout, layout.tiles[photoIndex]));
|
||||||
for(Attachment attachment:statusForContent.mediaAttachments){
|
}else if(attachment.type==Attachment.Type.GIFV){
|
||||||
if(attachment.type==Attachment.Type.IMAGE){
|
items.add(new GifVStatusDisplayItem(parentID, statusForContent, attachment, fragment, photoIndex, imageAttachments.size(), layout, layout.tiles[photoIndex]));
|
||||||
items.add(new PhotoStatusDisplayItem(parentID, statusForContent, attachment, fragment, photoIndex, totalPhotos));
|
}else if(attachment.type==Attachment.Type.VIDEO){
|
||||||
photoIndex++;
|
items.add(new VideoStatusDisplayItem(parentID, statusForContent, attachment, fragment, photoIndex, imageAttachments.size(), layout, layout.tiles[photoIndex]));
|
||||||
}else if(attachment.type==Attachment.Type.GIFV){
|
}else{
|
||||||
items.add(new GifVStatusDisplayItem(parentID, statusForContent, attachment, fragment, photoIndex, totalPhotos));
|
throw new IllegalStateException("This isn't supposed to happen, type is "+attachment.type);
|
||||||
photoIndex++;
|
}
|
||||||
}else if(attachment.type==Attachment.Type.VIDEO){
|
|
||||||
items.add(new VideoStatusDisplayItem(parentID, statusForContent, attachment, fragment, photoIndex, totalPhotos));
|
|
||||||
photoIndex++;
|
photoIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,13 @@ import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||||
|
|
||||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
|
|
||||||
public class VideoStatusDisplayItem extends ImageStatusDisplayItem{
|
public class VideoStatusDisplayItem extends ImageStatusDisplayItem{
|
||||||
public VideoStatusDisplayItem(String parentID, Status status, Attachment attachment, BaseStatusListFragment parentFragment, int index, int totalPhotos){
|
public VideoStatusDisplayItem(String parentID, Status status, Attachment attachment, BaseStatusListFragment parentFragment, int index, int totalPhotos, PhotoLayoutHelper.TiledLayoutResult tiledLayout, PhotoLayoutHelper.TiledLayoutResult.Tile thisTile){
|
||||||
super(parentID, parentFragment, attachment, status, index, totalPhotos);
|
super(parentID, parentFragment, attachment, status, index, totalPhotos, tiledLayout, thisTile);
|
||||||
request=new UrlImageLoaderRequest(attachment.previewUrl, 1000, 1000);
|
request=new UrlImageLoaderRequest(attachment.previewUrl, 1000, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package org.joinmastodon.android.ui.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
|
public class ImageAttachmentFrameLayout extends FrameLayout{
|
||||||
|
public static final int MAX_WIDTH=400; // dp
|
||||||
|
|
||||||
|
private PhotoLayoutHelper.TiledLayoutResult tileLayout;
|
||||||
|
private PhotoLayoutHelper.TiledLayoutResult.Tile tile;
|
||||||
|
|
||||||
|
public ImageAttachmentFrameLayout(@NonNull Context context){
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageAttachmentFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs){
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageAttachmentFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr){
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
|
||||||
|
if(isInEditMode()){
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int w=Math.min(((View)getParent()).getMeasuredWidth(), V.dp(MAX_WIDTH));
|
||||||
|
int actualHeight=Math.round(tile.height/1000f*w)+V.dp(1)*(tile.rowSpan-1);
|
||||||
|
int actualWidth=Math.round(tile.width/1000f*w);
|
||||||
|
if(tile.startCol+tile.colSpan<tileLayout.columnSizes.length)
|
||||||
|
actualWidth-=V.dp(1);
|
||||||
|
heightMeasureSpec=actualHeight | MeasureSpec.EXACTLY;
|
||||||
|
widthMeasureSpec=actualWidth | MeasureSpec.EXACTLY;
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLayout(PhotoLayoutHelper.TiledLayoutResult layout, PhotoLayoutHelper.TiledLayoutResult.Tile tile){
|
||||||
|
tileLayout=layout;
|
||||||
|
this.tile=tile;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/photo"
|
android:id="@+id/photo"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="250dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:scaleType="centerCrop"/>
|
android:scaleType="centerCrop"/>
|
||||||
|
|
||||||
|
@ -25,4 +25,4 @@
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
android:background="@drawable/ic_gif"/>
|
android:background="@drawable/ic_gif"/>
|
||||||
|
|
||||||
</FrameLayout>
|
</org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout>
|
|
@ -1,13 +1,13 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/photo"
|
android:id="@+id/photo"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="250dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:scaleType="centerCrop"/>
|
android:scaleType="centerCrop"/>
|
||||||
|
|
||||||
</FrameLayout>
|
</org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout>
|
|
@ -1,12 +1,12 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/photo"
|
android:id="@+id/photo"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="250dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:scaleType="centerCrop"/>
|
android:scaleType="centerCrop"/>
|
||||||
|
|
||||||
|
@ -18,4 +18,4 @@
|
||||||
android:elevation="3dp"
|
android:elevation="3dp"
|
||||||
android:background="@drawable/play_button"/>
|
android:background="@drawable/play_button"/>
|
||||||
|
|
||||||
</FrameLayout>
|
</org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout>
|
Loading…
Reference in New Issue