Add PagingMappingAdapter and convert GiphyMp4Adapter.

This commit is contained in:
Cody Henthorne 2021-12-16 14:31:01 -05:00 committed by Greyson Parrelli
parent dd79688f48
commit 5918227bff
6 changed files with 96 additions and 67 deletions

View file

@ -9,8 +9,9 @@ import androidx.annotation.Nullable;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.thoughtcrime.securesms.util.ByteUnit; import org.thoughtcrime.securesms.util.ByteUnit;
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel;
public class GiphyImage { public class GiphyImage implements MappingModel<GiphyImage> {
private static final int MAX_SIZE = (int) ByteUnit.MEGABYTES.toBytes(2); private static final int MAX_SIZE = (int) ByteUnit.MEGABYTES.toBytes(2);
@ -20,6 +21,16 @@ public class GiphyImage {
@JsonProperty("is_sticker") @JsonProperty("is_sticker")
private boolean isSticker; private boolean isSticker;
@Override
public boolean areItemsTheSame(@NonNull GiphyImage newItem) {
return getMp4Url().equals(newItem.getMp4Url());
}
@Override
public boolean areContentsTheSame(@NonNull GiphyImage newItem) {
return areItemsTheSame(newItem);
}
public boolean isSticker() { public boolean isSticker() {
return isSticker; return isSticker;
} }

View file

@ -1,77 +1,24 @@
package org.thoughtcrime.securesms.giph.mp4; package org.thoughtcrime.securesms.giph.mp4;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import org.signal.paging.PagingController;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.giph.model.GiphyImage; import org.thoughtcrime.securesms.giph.model.GiphyImage;
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory;
import java.util.Objects; import org.thoughtcrime.securesms.util.adapter.mapping.PagingMappingAdapter;
/** /**
* Maintains and displays a list of GiphyImage objects. This Adapter always displays gifs * Maintains and displays a list of GiphyImage objects. This Adapter always displays gifs
* as MP4 videos. * as MP4 videos.
*/ */
final class GiphyMp4Adapter extends ListAdapter<GiphyImage, GiphyMp4ViewHolder> { final class GiphyMp4Adapter extends PagingMappingAdapter<String> {
private final Callback listener;
private PagingController pagingController;
public GiphyMp4Adapter(@Nullable Callback listener) { public GiphyMp4Adapter(@Nullable Callback listener) {
super(new GiphyImageDiffUtilCallback()); registerFactory(GiphyImage.class, new LayoutFactory<>(v -> new GiphyMp4ViewHolder(v, listener), R.layout.giphy_mp4));
this.listener = listener;
}
@Override
public @NonNull GiphyMp4ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.giphy_mp4, parent, false);
return new GiphyMp4ViewHolder(itemView, listener);
}
@Override
public void onBindViewHolder(@NonNull GiphyMp4ViewHolder holder, int position) {
holder.onBind(getItem(position));
}
@Override
protected GiphyImage getItem(int position) {
if (pagingController != null) {
pagingController.onDataNeededAroundIndex(position);
}
return super.getItem(position);
}
void setPagingController(@Nullable PagingController pagingController) {
this.pagingController = pagingController;
} }
interface Callback { interface Callback {
void onClick(@NonNull GiphyImage giphyImage); void onClick(@NonNull GiphyImage giphyImage);
} }
private static final class GiphyImageDiffUtilCallback extends DiffUtil.ItemCallback<GiphyImage> {
@Override
public boolean areItemsTheSame(@NonNull GiphyImage oldItem, @NonNull GiphyImage newItem) {
return Objects.equals(oldItem.getMp4Url(), newItem.getMp4Url());
}
@Override
public boolean areContentsTheSame(@NonNull GiphyImage oldItem, @NonNull GiphyImage newItem) {
return areItemsTheSame(oldItem, newItem);
}
}
} }

View file

@ -9,7 +9,6 @@ import android.widget.ImageView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
@ -24,11 +23,12 @@ import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.util.Projection; import org.thoughtcrime.securesms.util.Projection;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder;
/** /**
* Holds a view which will either play back an MP4 gif or show its still. * Holds a view which will either play back an MP4 gif or show its still.
*/ */
final class GiphyMp4ViewHolder extends RecyclerView.ViewHolder implements GiphyMp4Playable { final class GiphyMp4ViewHolder extends MappingViewHolder<GiphyImage> implements GiphyMp4Playable {
private static final Projection.Corners CORNERS = new Projection.Corners(ViewUtil.dpToPx(8)); private static final Projection.Corners CORNERS = new Projection.Corners(ViewUtil.dpToPx(8));
@ -52,7 +52,8 @@ final class GiphyMp4ViewHolder extends RecyclerView.ViewHolder implements GiphyM
container.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIXED_WIDTH); container.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIXED_WIDTH);
} }
void onBind(@NonNull GiphyImage giphyImage) { @Override
public void bind(@NonNull GiphyImage giphyImage) {
aspectRatio = giphyImage.getGifAspectRatio(); aspectRatio = giphyImage.getGifAspectRatio();
mediaItem = MediaItem.fromUri(Uri.parse(giphyImage.getMp4PreviewUrl())); mediaItem = MediaItem.fromUri(Uri.parse(giphyImage.getMp4PreviewUrl()));

View file

@ -17,6 +17,7 @@ import org.signal.paging.PagingConfig;
import org.signal.paging.PagingController; import org.signal.paging.PagingController;
import org.thoughtcrime.securesms.giph.model.GiphyImage; import org.thoughtcrime.securesms.giph.model.GiphyImage;
import org.thoughtcrime.securesms.util.DefaultValueLiveData; import org.thoughtcrime.securesms.util.DefaultValueLiveData;
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModelList;
import org.thoughtcrime.securesms.util.SingleLiveEvent; import org.thoughtcrime.securesms.util.SingleLiveEvent;
import java.util.List; import java.util.List;
@ -30,7 +31,7 @@ public final class GiphyMp4ViewModel extends ViewModel {
private final GiphyMp4Repository repository; private final GiphyMp4Repository repository;
private final MutableLiveData<PagedData<String, GiphyImage>> pagedData; private final MutableLiveData<PagedData<String, GiphyImage>> pagedData;
private final LiveData<List<GiphyImage>> images; private final LiveData<MappingModelList> images;
private final LiveData<PagingController<String>> pagingController; private final LiveData<PagingController<String>> pagingController;
private final SingleLiveEvent<GiphyMp4SaveResult> saveResultEvents; private final SingleLiveEvent<GiphyMp4SaveResult> saveResultEvents;
private final boolean isForMms; private final boolean isForMms;
@ -49,7 +50,7 @@ public final class GiphyMp4ViewModel extends ViewModel {
.filterNot(g -> TextUtils.isEmpty(isForMms ? g.getGifMmsUrl() : g.getGifUrl())) .filterNot(g -> TextUtils.isEmpty(isForMms ? g.getGifMmsUrl() : g.getGifUrl()))
.filterNot(g -> TextUtils.isEmpty(g.getMp4PreviewUrl())) .filterNot(g -> TextUtils.isEmpty(g.getMp4PreviewUrl()))
.filterNot(g -> TextUtils.isEmpty(g.getStillUrl())) .filterNot(g -> TextUtils.isEmpty(g.getStillUrl()))
.toList())); .collect(MappingModelList.toMappingModelList())));
} }
LiveData<PagedData<String, GiphyImage>> getPagedData() { LiveData<PagedData<String, GiphyImage>> getPagedData() {
@ -73,7 +74,7 @@ public final class GiphyMp4ViewModel extends ViewModel {
return saveResultEvents; return saveResultEvents;
} }
public @NonNull LiveData<List<GiphyImage>> getImages() { public @NonNull LiveData<MappingModelList> getImages() {
return images; return images;
} }

View file

@ -44,9 +44,9 @@ import kotlin.jvm.functions.Function1;
*/ */
public class MappingAdapter extends ListAdapter<MappingModel<?>, MappingViewHolder<?>> { public class MappingAdapter extends ListAdapter<MappingModel<?>, MappingViewHolder<?>> {
private final Map<Integer, Factory<?>> factories; final Map<Integer, Factory<?>> factories;
private final Map<Class<?>, Integer> itemTypes; final Map<Class<?>, Integer> itemTypes;
private int typeCount; int typeCount;
public MappingAdapter() { public MappingAdapter() {
super(new MappingDiffCallback()); super(new MappingDiffCallback());

View file

@ -0,0 +1,69 @@
package org.thoughtcrime.securesms.util.adapter.mapping;
import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.paging.PagingController;
import org.thoughtcrime.securesms.util.ViewUtil;
/**
* A specialized {@link MappingAdapter} backed by a {@link PagingController}.
*/
public class PagingMappingAdapter<Key> extends MappingAdapter {
private PagingController<Key> pagingController;
public PagingMappingAdapter() {
this(1, ViewUtil.dpToPx(100));
}
public PagingMappingAdapter(int placeHolderWidth, int placeHolderHeight) {
registerFactory(Placeholder.class, parent -> {
View view = new FrameLayout(parent.getContext());
view.setLayoutParams(new FrameLayout.LayoutParams(placeHolderWidth, placeHolderHeight));
return new MappingViewHolder.SimpleViewHolder<>(view);
});
}
public void setPagingController(@Nullable PagingController<Key> pagingController) {
this.pagingController = pagingController;
}
@Override
protected @Nullable MappingModel<?> getItem(int position) {
if (pagingController != null) {
pagingController.onDataNeededAroundIndex(position);
}
return super.getItem(position);
}
@Override
public int getItemViewType(int position) {
MappingModel<?> item = getItem(position);
if (item == null) {
//noinspection ConstantConditions
return itemTypes.get(Placeholder.class);
}
Integer type = itemTypes.get(item.getClass());
if (type != null) {
return type;
}
throw new AssertionError("No view holder factory for type: " + item.getClass());
}
private static class Placeholder implements MappingModel<Placeholder> {
@Override
public boolean areItemsTheSame(@NonNull Placeholder newItem) {
return false;
}
@Override
public boolean areContentsTheSame(@NonNull Placeholder newItem) {
return false;
}
}
}