diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/model/GiphyImage.java b/app/src/main/java/org/thoughtcrime/securesms/giph/model/GiphyImage.java index 018b925384..13a6859f2c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/model/GiphyImage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/model/GiphyImage.java @@ -9,8 +9,9 @@ import androidx.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonProperty; import org.thoughtcrime.securesms.util.ByteUnit; +import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel; -public class GiphyImage { +public class GiphyImage implements MappingModel { private static final int MAX_SIZE = (int) ByteUnit.MEGABYTES.toBytes(2); @@ -20,6 +21,16 @@ public class GiphyImage { @JsonProperty("is_sticker") 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() { return isSticker; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4Adapter.java b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4Adapter.java index e163014ca0..4f9f30c89e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4Adapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4Adapter.java @@ -1,77 +1,24 @@ 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.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.giph.model.GiphyImage; - -import java.util.Objects; +import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory; +import org.thoughtcrime.securesms.util.adapter.mapping.PagingMappingAdapter; /** * Maintains and displays a list of GiphyImage objects. This Adapter always displays gifs * as MP4 videos. */ -final class GiphyMp4Adapter extends ListAdapter { - - private final Callback listener; - - private PagingController pagingController; - +final class GiphyMp4Adapter extends PagingMappingAdapter { public GiphyMp4Adapter(@Nullable Callback listener) { - super(new GiphyImageDiffUtilCallback()); - - 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; + registerFactory(GiphyImage.class, new LayoutFactory<>(v -> new GiphyMp4ViewHolder(v, listener), R.layout.giphy_mp4)); } interface Callback { void onClick(@NonNull GiphyImage giphyImage); } - - private static final class GiphyImageDiffUtilCallback extends DiffUtil.ItemCallback { - - @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); - } - } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4ViewHolder.java b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4ViewHolder.java index f7bf602881..7b944f02d1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4ViewHolder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4ViewHolder.java @@ -9,7 +9,6 @@ import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.load.engine.DiskCacheStrategy; 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.Util; 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. */ -final class GiphyMp4ViewHolder extends RecyclerView.ViewHolder implements GiphyMp4Playable { +final class GiphyMp4ViewHolder extends MappingViewHolder implements GiphyMp4Playable { 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); } - void onBind(@NonNull GiphyImage giphyImage) { + @Override + public void bind(@NonNull GiphyImage giphyImage) { aspectRatio = giphyImage.getGifAspectRatio(); mediaItem = MediaItem.fromUri(Uri.parse(giphyImage.getMp4PreviewUrl())); diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4ViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4ViewModel.java index 8492860d7d..5f82fd8409 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4ViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4ViewModel.java @@ -17,6 +17,7 @@ import org.signal.paging.PagingConfig; import org.signal.paging.PagingController; import org.thoughtcrime.securesms.giph.model.GiphyImage; import org.thoughtcrime.securesms.util.DefaultValueLiveData; +import org.thoughtcrime.securesms.util.adapter.mapping.MappingModelList; import org.thoughtcrime.securesms.util.SingleLiveEvent; import java.util.List; @@ -30,7 +31,7 @@ public final class GiphyMp4ViewModel extends ViewModel { private final GiphyMp4Repository repository; private final MutableLiveData> pagedData; - private final LiveData> images; + private final LiveData images; private final LiveData> pagingController; private final SingleLiveEvent saveResultEvents; 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(g.getMp4PreviewUrl())) .filterNot(g -> TextUtils.isEmpty(g.getStillUrl())) - .toList())); + .collect(MappingModelList.toMappingModelList()))); } LiveData> getPagedData() { @@ -73,7 +74,7 @@ public final class GiphyMp4ViewModel extends ViewModel { return saveResultEvents; } - public @NonNull LiveData> getImages() { + public @NonNull LiveData getImages() { return images; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter.java index b215bde6d3..d16bf36085 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter.java @@ -44,9 +44,9 @@ import kotlin.jvm.functions.Function1; */ public class MappingAdapter extends ListAdapter, MappingViewHolder> { - private final Map> factories; - private final Map, Integer> itemTypes; - private int typeCount; + final Map> factories; + final Map, Integer> itemTypes; + int typeCount; public MappingAdapter() { super(new MappingDiffCallback()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/adapter/mapping/PagingMappingAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/util/adapter/mapping/PagingMappingAdapter.java new file mode 100644 index 0000000000..a162474e92 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/adapter/mapping/PagingMappingAdapter.java @@ -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 extends MappingAdapter { + + private PagingController 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 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 { + @Override + public boolean areItemsTheSame(@NonNull Placeholder newItem) { + return false; + } + + @Override + public boolean areContentsTheSame(@NonNull Placeholder newItem) { + return false; + } + } +}