Polish thumbnail animation.

This commit is contained in:
Alex Hart
2023-02-14 10:08:17 -04:00
committed by Greyson Parrelli
parent 417db2341b
commit c027203e8c
5 changed files with 117 additions and 22 deletions

View File

@@ -1,9 +1,8 @@
package org.thoughtcrime.securesms.components; package org.thoughtcrime.securesms.components;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.TextView; import android.widget.TextView;
@@ -29,13 +28,13 @@ public class AlbumThumbnailView extends FrameLayout {
private int currentSizeClass; private int currentSizeClass;
private final int[] corners = new int[4];
private ViewGroup albumCellContainer; private ViewGroup albumCellContainer;
private Stub<TransferControlView> transferControls; private Stub<TransferControlView> transferControls;
private Drawable imageDrawable;
private final SlideClickListener defaultThumbnailClickListener = (v, slide) -> { private final SlideClickListener defaultThumbnailClickListener = (v, slide) -> {
if (thumbnailClickListener != null) { if (thumbnailClickListener != null) {
imageDrawable = ((ThumbnailView) v).getImageDrawable();
thumbnailClickListener.onClick(v, slide); thumbnailClickListener.onClick(v, slide);
} }
}; };
@@ -86,10 +85,7 @@ public class AlbumThumbnailView extends FrameLayout {
} }
showSlides(glideRequests, slides); showSlides(glideRequests, slides);
} applyCorners();
public @Nullable Drawable getDrawable() {
return imageDrawable;
} }
public void setCellBackgroundColor(@ColorInt int color) { public void setCellBackgroundColor(@ColorInt int color) {
@@ -110,6 +106,15 @@ public class AlbumThumbnailView extends FrameLayout {
downloadClickListener = listener; downloadClickListener = listener;
} }
public void setRadii(int topLeft, int topRight, int bottomRight, int bottomLeft) {
corners[0] = topLeft;
corners[1] = topRight;
corners[2] = bottomRight;
corners[3] = bottomLeft;
applyCorners();
}
private void inflateLayout(int sizeClass) { private void inflateLayout(int sizeClass) {
albumCellContainer.removeAllViews(); albumCellContainer.removeAllViews();
@@ -132,6 +137,73 @@ public class AlbumThumbnailView extends FrameLayout {
} }
} }
private void applyCorners() {
if (currentSizeClass < 2) {
return;
}
switch (currentSizeClass) {
case 2:
applyCornersForSizeClass2();
break;
case 3:
applyCornersForSizeClass3();
break;
case 4:
applyCornersForSizeClass4();
break;
case 5:
applyCornersForSizeClass5();
break;
default:
applyCornersForManySizeClass();
}
}
private ThumbnailView[] getCells() {
ThumbnailView one = findViewById(R.id.album_cell_1);
ThumbnailView two = findViewById(R.id.album_cell_2);
ThumbnailView three = findViewById(R.id.album_cell_3);
ThumbnailView four = findViewById(R.id.album_cell_4);
ThumbnailView five = findViewById(R.id.album_cell_5);
return new ThumbnailView[]{one, two, three, four, five};
}
private void applyCornersForSizeClass2() {
ThumbnailView[] cells = getCells();
cells[0].setRadii(corners[0], 0, 0, corners[3]);
cells[1].setRadii(0, corners[1], corners[2], 0);
}
private void applyCornersForSizeClass3() {
ThumbnailView[] cells = getCells();
cells[0].setRadii(corners[0], 0, 0, corners[3]);
cells[1].setRadii(0, corners[1], 0, 0);
cells[2].setRadii(0, 0, corners[2], 0);
}
private void applyCornersForSizeClass4() {
ThumbnailView[] cells = getCells();
cells[0].setRadii(corners[0], 0, 0, 0);
cells[1].setRadii(0, corners[1], 0, 0);
cells[2].setRadii(0, 0, 0, corners[3]);
cells[3].setRadii(0, 0, corners[2], 0);
}
private void applyCornersForSizeClass5() {
ThumbnailView[] cells = getCells();
cells[0].setRadii(corners[0], 0, 0, 0);
cells[1].setRadii(0, corners[1], 0, 0);
cells[2].setRadii(0, 0, 0, corners[3]);
cells[3].setRadii(0, 0, 0, 0);
cells[4].setRadii(0, 0, corners[2], 0);
}
private void applyCornersForManySizeClass() {
applyCornersForSizeClass5();
}
private void showSlides(@NonNull GlideRequests glideRequests, @NonNull List<Slide> slides) { private void showSlides(@NonNull GlideRequests glideRequests, @NonNull List<Slide> slides) {
setSlide(glideRequests, slides.get(0), R.id.album_cell_1); setSlide(glideRequests, slides.get(0), R.id.album_cell_1);
setSlide(glideRequests, slides.get(1), R.id.album_cell_2); setSlide(glideRequests, slides.get(1), R.id.album_cell_2);

View File

@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.components
import android.content.Context import android.content.Context
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.util.AttributeSet import android.util.AttributeSet
@@ -131,14 +130,6 @@ class ConversationItemThumbnail @JvmOverloads constructor(
state.applyState(thumbnail, album) state.applyState(thumbnail, album)
} }
fun getDrawable(): Drawable? {
return if (thumbnail.resolved()) {
thumbnail.get().imageDrawable
} else {
album.get().drawable
}
}
fun hideThumbnailView() { fun hideThumbnailView() {
state = state.copy(thumbnailViewState = state.thumbnailViewState.copy(alpha = 0f)) state = state.copy(thumbnailViewState = state.thumbnailViewState.copy(alpha = 0f))
state.thumbnailViewState.applyState(thumbnail) state.thumbnailViewState.applyState(thumbnail)
@@ -159,6 +150,22 @@ class ConversationItemThumbnail @JvmOverloads constructor(
fun setCorners(topLeft: Int, topRight: Int, bottomRight: Int, bottomLeft: Int) { fun setCorners(topLeft: Int, topRight: Int, bottomRight: Int, bottomLeft: Int) {
cornerMask.setRadii(topLeft, topRight, bottomRight, bottomLeft) cornerMask.setRadii(topLeft, topRight, bottomRight, bottomLeft)
state = state.copy(
thumbnailViewState = state.thumbnailViewState.copy(
cornerTopLeft = topLeft,
cornerTopRight = topRight,
cornerBottomRight = bottomRight,
cornerBottomLeft = bottomLeft
),
albumViewState = state.albumViewState.copy(
cornerTopLeft = topLeft,
cornerTopRight = topRight,
cornerBottomRight = bottomRight,
cornerBottomLeft = bottomLeft
)
)
state.applyState(thumbnail, album)
} }
fun setMinimumThumbnailWidth(@Px width: Int) { fun setMinimumThumbnailWidth(@Px width: Int) {

View File

@@ -11,7 +11,7 @@ import org.thoughtcrime.securesms.mms.SlidesClickedListener
import org.thoughtcrime.securesms.util.views.Stub import org.thoughtcrime.securesms.util.views.Stub
/** /**
* Parcelizable state object for [ConversationItemThumbnail] * Parcelable state object for [ConversationItemThumbnail]
* This allows us to manage inputs for [ThumbnailView] and [AlbumThumbnailView] without * This allows us to manage inputs for [ThumbnailView] and [AlbumThumbnailView] without
* actually having them inflated. When the views are finally inflated, we 'apply' * actually having them inflated. When the views are finally inflated, we 'apply'
*/ */
@@ -38,7 +38,11 @@ data class ConversationItemThumbnailState(
private val minWidth: Int = -1, private val minWidth: Int = -1,
private val maxWidth: Int = -1, private val maxWidth: Int = -1,
private val minHeight: Int = -1, private val minHeight: Int = -1,
private val maxHeight: Int = -1 private val maxHeight: Int = -1,
private val cornerTopLeft: Int = 0,
private val cornerTopRight: Int = 0,
private val cornerBottomRight: Int = 0,
private val cornerBottomLeft: Int = 0
) : Parcelable { ) : Parcelable {
fun applyState(thumbnailView: Stub<ThumbnailView>) { fun applyState(thumbnailView: Stub<ThumbnailView>) {
@@ -50,6 +54,7 @@ data class ConversationItemThumbnailState(
thumbnailView.get().alpha = alpha thumbnailView.get().alpha = alpha
thumbnailView.get().isFocusable = focusable thumbnailView.get().isFocusable = focusable
thumbnailView.get().isClickable = clickable thumbnailView.get().isClickable = clickable
thumbnailView.get().setRadii(cornerTopLeft, cornerTopRight, cornerBottomRight, cornerBottomLeft)
thumbnailView.get().setThumbnailClickListener(clickListener) thumbnailView.get().setThumbnailClickListener(clickListener)
thumbnailView.get().setDownloadClickListener(downloadClickListener) thumbnailView.get().setDownloadClickListener(downloadClickListener)
thumbnailView.get().setOnLongClickListener(longClickListener) thumbnailView.get().setOnLongClickListener(longClickListener)
@@ -70,7 +75,11 @@ data class ConversationItemThumbnailState(
@IgnoredOnParcel @IgnoredOnParcel
private val longClickListener: OnLongClickListener? = null, private val longClickListener: OnLongClickListener? = null,
private val visibility: Int = View.GONE, private val visibility: Int = View.GONE,
private val cellBackgroundColor: Int = Color.TRANSPARENT private val cellBackgroundColor: Int = Color.TRANSPARENT,
private val cornerTopLeft: Int = 0,
private val cornerTopRight: Int = 0,
private val cornerBottomRight: Int = 0,
private val cornerBottomLeft: Int = 0
) : Parcelable { ) : Parcelable {
fun applyState(albumView: Stub<AlbumThumbnailView>) { fun applyState(albumView: Stub<AlbumThumbnailView>) {
@@ -81,6 +90,7 @@ data class ConversationItemThumbnailState(
albumView.get().isFocusable = focusable albumView.get().isFocusable = focusable
albumView.get().isClickable = clickable albumView.get().isClickable = clickable
albumView.get().setRadii(cornerTopLeft, cornerTopRight, cornerBottomRight, cornerBottomLeft)
albumView.get().setThumbnailClickListener(clickListener) albumView.get().setThumbnailClickListener(clickListener)
albumView.get().setDownloadClickListener(downloadClickListener) albumView.get().setDownloadClickListener(downloadClickListener)
albumView.get().setOnLongClickListener(longClickListener) albumView.get().setOnLongClickListener(longClickListener)

View File

@@ -529,6 +529,11 @@ public class ThumbnailView extends FrameLayout {
invalidate(); invalidate();
} }
public void setRadii(int topLeft, int topRight, int bottomRight, int bottomLeft) {
cornerMask.setRadii(topLeft, topRight, bottomRight, bottomLeft);
invalidate();
}
private GlideRequest<Drawable> buildThumbnailGlideRequest(@NonNull GlideRequests glideRequests, @NonNull Slide slide) { private GlideRequest<Drawable> buildThumbnailGlideRequest(@NonNull GlideRequests glideRequests, @NonNull Slide slide) {
GlideRequest<Drawable> request = applySizing(glideRequests.load(new DecryptableUri(Objects.requireNonNull(slide.getUri()))) GlideRequest<Drawable> request = applySizing(glideRequests.load(new DecryptableUri(Objects.requireNonNull(slide.getUri())))
.diskCacheStrategy(DiskCacheStrategy.RESOURCE) .diskCacheStrategy(DiskCacheStrategy.RESOURCE)

View File

@@ -87,6 +87,7 @@ import org.thoughtcrime.securesms.components.Outliner;
import org.thoughtcrime.securesms.components.PlaybackSpeedToggleTextView; import org.thoughtcrime.securesms.components.PlaybackSpeedToggleTextView;
import org.thoughtcrime.securesms.components.QuoteView; import org.thoughtcrime.securesms.components.QuoteView;
import org.thoughtcrime.securesms.components.SharedContactView; import org.thoughtcrime.securesms.components.SharedContactView;
import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView; import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
import org.thoughtcrime.securesms.components.mention.MentionAnnotation; import org.thoughtcrime.securesms.components.mention.MentionAnnotation;
import org.thoughtcrime.securesms.contactshare.Contact; import org.thoughtcrime.securesms.contactshare.Contact;
@@ -2393,8 +2394,8 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
mediaThumbnailStub.require().getCorners().getBottomRight(), mediaThumbnailStub.require().getCorners().getBottomRight(),
mediaThumbnailStub.require().getCorners().getBottomLeft() mediaThumbnailStub.require().getCorners().getBottomLeft()
)); ));
MediaPreviewCache.INSTANCE.setDrawable(mediaThumbnailStub.require().getDrawable()); MediaPreviewCache.INSTANCE.setDrawable(((ThumbnailView) v).getImageDrawable());
eventListener.goToMediaPreview(ConversationItem.this, mediaThumbnailStub.require(), args); eventListener.goToMediaPreview(ConversationItem.this, v, args);
} else if (slide.getUri() != null) { } else if (slide.getUri() != null) {
Log.i(TAG, "Clicked: " + slide.getUri() + " , " + slide.getContentType()); Log.i(TAG, "Clicked: " + slide.getUri() + " , " + slide.getContentType());
Uri publicUri = PartAuthority.getAttachmentPublicUri(slide.getUri()); Uri publicUri = PartAuthority.getAttachmentPublicUri(slide.getUri());