mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 17:29:32 +01:00
Speed up thumbnail transition.
This commit is contained in:
committed by
Greyson Parrelli
parent
d9c31a6cd6
commit
d7c3112602
@@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
@@ -29,9 +30,11 @@ public class AlbumThumbnailView extends FrameLayout {
|
||||
|
||||
private ViewGroup albumCellContainer;
|
||||
private Stub<TransferControlView> transferControls;
|
||||
private Bitmap bitmap;
|
||||
|
||||
private final SlideClickListener defaultThumbnailClickListener = (v, slide) -> {
|
||||
if (thumbnailClickListener != null) {
|
||||
bitmap = ((ThumbnailView) v).getBitmap();
|
||||
thumbnailClickListener.onClick(v, slide);
|
||||
}
|
||||
};
|
||||
@@ -84,6 +87,10 @@ public class AlbumThumbnailView extends FrameLayout {
|
||||
showSlides(glideRequests, slides);
|
||||
}
|
||||
|
||||
public @Nullable Bitmap getBitmap() {
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public void setCellBackgroundColor(@ColorInt int color) {
|
||||
ViewGroup cellRoot = findViewById(R.id.album_thumbnail_root);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -58,10 +59,10 @@ public class BorderlessImageView extends FrameLayout {
|
||||
boolean showControls = slide.asAttachment().getUri() == null;
|
||||
|
||||
if (slide.hasSticker()) {
|
||||
image.setFit(new CenterInside());
|
||||
image.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
|
||||
image.setImageResource(glideRequests, slide, showControls, false);
|
||||
} else {
|
||||
image.setFit(new CenterCrop());
|
||||
image.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
image.setImageResource(glideRequests, slide, showControls, false, slide.asAttachment().getWidth(), slide.asAttachment().getHeight());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms.components
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
@@ -130,6 +131,14 @@ class ConversationItemThumbnail @JvmOverloads constructor(
|
||||
state.applyState(thumbnail, album)
|
||||
}
|
||||
|
||||
fun getBitmap(): Bitmap? {
|
||||
return if (thumbnail.resolved()) {
|
||||
thumbnail.get().bitmap
|
||||
} else {
|
||||
album.get().bitmap
|
||||
}
|
||||
}
|
||||
|
||||
fun hideThumbnailView() {
|
||||
state = state.copy(thumbnailViewState = state.thumbnailViewState.copy(alpha = 0f))
|
||||
state.thumbnailViewState.applyState(thumbnail)
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
||||
import org.thoughtcrime.securesms.database.MediaTable;
|
||||
import org.thoughtcrime.securesms.mediapreview.MediaPreviewCache;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
@@ -97,6 +98,7 @@ public class ThreadPhotoRailView extends FrameLayout {
|
||||
}
|
||||
|
||||
imageView.setOnClickListener(v -> {
|
||||
MediaPreviewCache.INSTANCE.setBitmap(imageView.getBitmap());
|
||||
if (clickedListener != null) clickedListener.onItemClicked(imageView, mediaRecord);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ package org.thoughtcrime.securesms.components;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
@@ -23,12 +25,10 @@ import androidx.appcompat.widget.AppCompatImageView;
|
||||
import com.bumptech.glide.RequestBuilder;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
|
||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
|
||||
import com.bumptech.glide.load.resource.bitmap.FitCenter;
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
||||
import com.bumptech.glide.request.Request;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
@@ -67,25 +67,26 @@ public class ThumbnailView extends FrameLayout {
|
||||
private static final int MIN_HEIGHT = 2;
|
||||
private static final int MAX_HEIGHT = 3;
|
||||
|
||||
private Bitmap imageBitmap;
|
||||
private final ImageView image;
|
||||
private final ImageView blurhash;
|
||||
private final View playOverlay;
|
||||
private final View captionIcon;
|
||||
private final AppCompatImageView errorImage;
|
||||
|
||||
private OnClickListener parentClickListener;
|
||||
private OnClickListener parentClickListener;
|
||||
|
||||
private final int[] dimens = new int[2];
|
||||
private final int[] bounds = new int[4];
|
||||
private final int[] measureDimens = new int[2];
|
||||
|
||||
private final CornerMask cornerMask;
|
||||
|
||||
private Optional<TransferControlView> transferControls = Optional.empty();
|
||||
private SlideClickListener thumbnailClickListener = null;
|
||||
private SlidesClickedListener downloadClickListener = null;
|
||||
private Slide slide = null;
|
||||
private BitmapTransformation fit = new CenterCrop();
|
||||
|
||||
private int radius;
|
||||
|
||||
public ThumbnailView(Context context) {
|
||||
this(context, null);
|
||||
@@ -105,6 +106,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
this.playOverlay = findViewById(R.id.play_overlay);
|
||||
this.captionIcon = findViewById(R.id.thumbnail_caption_icon);
|
||||
this.errorImage = findViewById(R.id.thumbnail_error);
|
||||
this.cornerMask = new CornerMask(this);
|
||||
|
||||
super.setOnClickListener(new ThumbnailClickDispatcher());
|
||||
|
||||
@@ -114,8 +116,9 @@ public class ThumbnailView extends FrameLayout {
|
||||
bounds[MAX_WIDTH] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxWidth, 0);
|
||||
bounds[MIN_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minHeight, 0);
|
||||
bounds[MAX_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxHeight, 0);
|
||||
radius = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_thumbnail_radius, getResources().getDimensionPixelSize(R.dimen.thumbnail_default_radius));
|
||||
fit = typedArray.getInt(R.styleable.ThumbnailView_thumbnail_fit, 0) == 1 ? new FitCenter() : new CenterCrop();
|
||||
|
||||
float radius = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_thumbnail_radius, getResources().getDimensionPixelSize(R.dimen.thumbnail_default_radius));
|
||||
cornerMask.setRadius((int) radius);
|
||||
|
||||
int transparentOverlayColor = typedArray.getColor(R.styleable.ThumbnailView_transparent_overlay_color, -1);
|
||||
if (transparentOverlayColor > 0) {
|
||||
@@ -126,7 +129,8 @@ public class ThumbnailView extends FrameLayout {
|
||||
|
||||
typedArray.recycle();
|
||||
} else {
|
||||
radius = getResources().getDimensionPixelSize(R.dimen.message_corner_collapse_radius);
|
||||
float radius = getResources().getDimensionPixelSize(R.dimen.message_corner_collapse_radius);
|
||||
cornerMask.setRadius((int) radius);
|
||||
image.setColorFilter(null);
|
||||
}
|
||||
}
|
||||
@@ -156,7 +160,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
|
||||
if (playOverlayWidth * 2 > getWidth()) {
|
||||
playOverlayScale /= 2;
|
||||
captionIconScale = 0;
|
||||
captionIconScale = 0;
|
||||
}
|
||||
|
||||
playOverlay.setScaleX(playOverlayScale);
|
||||
@@ -166,6 +170,13 @@ public class ThumbnailView extends FrameLayout {
|
||||
captionIcon.setScaleY(captionIconScale);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
super.dispatchDraw(canvas);
|
||||
|
||||
cornerMask.mask(canvas);
|
||||
}
|
||||
|
||||
public void setMinimumThumbnailWidth(@Px int width) {
|
||||
bounds[MIN_WIDTH] = width;
|
||||
invalidate();
|
||||
@@ -187,7 +198,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
}
|
||||
|
||||
if (dimensAreInvalid || dimensFilledCount == 0 || boundsFilledCount == 0) {
|
||||
targetDimens[WIDTH] = 0;
|
||||
targetDimens[WIDTH] = 0;
|
||||
targetDimens[HEIGHT] = 0;
|
||||
return;
|
||||
}
|
||||
@@ -219,10 +230,10 @@ public class ThumbnailView extends FrameLayout {
|
||||
|
||||
if (maxWidthRatio > 1 || maxHeightRatio > 1) {
|
||||
if (maxWidthRatio >= maxHeightRatio) {
|
||||
measuredWidth /= maxWidthRatio;
|
||||
measuredWidth /= maxWidthRatio;
|
||||
measuredHeight /= maxWidthRatio;
|
||||
} else {
|
||||
measuredWidth /= maxHeightRatio;
|
||||
measuredWidth /= maxHeightRatio;
|
||||
measuredHeight /= maxHeightRatio;
|
||||
}
|
||||
|
||||
@@ -231,10 +242,10 @@ public class ThumbnailView extends FrameLayout {
|
||||
|
||||
} else if (minWidthRatio < 1 || minHeightRatio < 1) {
|
||||
if (minWidthRatio <= minHeightRatio) {
|
||||
measuredWidth /= minWidthRatio;
|
||||
measuredWidth /= minWidthRatio;
|
||||
measuredHeight /= minWidthRatio;
|
||||
} else {
|
||||
measuredWidth /= minHeightRatio;
|
||||
measuredWidth /= minHeightRatio;
|
||||
measuredHeight /= minHeightRatio;
|
||||
}
|
||||
|
||||
@@ -274,6 +285,10 @@ public class ThumbnailView extends FrameLayout {
|
||||
if (transferControls.isPresent()) transferControls.get().setClickable(clickable);
|
||||
}
|
||||
|
||||
public @Nullable Bitmap getBitmap() {
|
||||
return imageBitmap;
|
||||
}
|
||||
|
||||
private TransferControlView getTransferControls() {
|
||||
if (!transferControls.isPresent()) {
|
||||
transferControls = Optional.of(ViewUtil.inflateStub(this, R.id.transfer_controls_stub));
|
||||
@@ -358,7 +373,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
return new SettableFuture<>(false);
|
||||
}
|
||||
|
||||
if (this.slide != null && this.slide.getFastPreflightId() != null &&
|
||||
if (this.slide != null && this.slide.getFastPreflightId() != null &&
|
||||
(!slide.hasVideo() || Util.equals(this.slide.getUri(), slide.getUri())) &&
|
||||
Util.equals(this.slide.getFastPreflightId(), slide.getFastPreflightId()))
|
||||
{
|
||||
@@ -400,7 +415,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
thumbnailFuture.addListener(new BlurhashClearListener(glideRequests, blurhash));
|
||||
}
|
||||
|
||||
buildThumbnailGlideRequest(glideRequests, slide).into(new GlideDrawableListeningTarget(image, result));
|
||||
buildThumbnailGlideRequest(glideRequests, slide).into(new BitmapCaptor(image, result));
|
||||
|
||||
resultHandled = true;
|
||||
} else {
|
||||
@@ -440,15 +455,9 @@ public class ThumbnailView extends FrameLayout {
|
||||
request = request.override(width, height);
|
||||
}
|
||||
|
||||
if (radius > 0) {
|
||||
request = request.transforms(new CenterCrop(), new RoundedCorners(radius));
|
||||
} else {
|
||||
request = request.transforms(new CenterCrop());
|
||||
}
|
||||
|
||||
GlideDrawableListeningTarget target = new GlideDrawableListeningTarget(image, future);
|
||||
Request previousRequest = target.getRequest();
|
||||
boolean previousRequestRunning = previousRequest != null && previousRequest.isRunning();
|
||||
GlideDrawableListeningTarget target = new BitmapCaptor(image, future);
|
||||
Request previousRequest = target.getRequest();
|
||||
boolean previousRequestRunning = previousRequest != null && previousRequest.isRunning();
|
||||
request.into(target);
|
||||
if (listener != null) {
|
||||
listener.onLoadScheduled();
|
||||
@@ -476,13 +485,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
request = request.override(width, height);
|
||||
}
|
||||
|
||||
if (radius > 0) {
|
||||
request = request.transforms(new CenterCrop(), new RoundedCorners(radius));
|
||||
} else {
|
||||
request = request.transforms(new CenterCrop());
|
||||
}
|
||||
|
||||
request.into(new GlideDrawableListeningTarget(image, future));
|
||||
request.into(new BitmapCaptor(image, future));
|
||||
blurhash.setImageDrawable(null);
|
||||
|
||||
return future;
|
||||
@@ -518,23 +521,27 @@ public class ThumbnailView extends FrameLayout {
|
||||
getTransferControls().showProgressSpinner();
|
||||
}
|
||||
|
||||
public void setFit(@NonNull BitmapTransformation fit) {
|
||||
this.fit = fit;
|
||||
public void setScaleType(@NonNull ImageView.ScaleType scaleType) {
|
||||
image.setScaleType(scaleType);
|
||||
}
|
||||
|
||||
protected void setRadius(int radius) {
|
||||
this.radius = radius;
|
||||
cornerMask.setRadius(radius);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private GlideRequest buildThumbnailGlideRequest(@NonNull GlideRequests glideRequests, @NonNull Slide slide) {
|
||||
GlideRequest request = applySizing(glideRequests.load(new DecryptableUri(slide.getUri()))
|
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||
.transition(withCrossFade()), fit);
|
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||
.transition(withCrossFade()));
|
||||
|
||||
boolean doNotShowMissingThumbnailImage = Build.VERSION.SDK_INT < 23;
|
||||
|
||||
if (slide.isInProgress() || doNotShowMissingThumbnailImage) return request;
|
||||
else return request.apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture));
|
||||
if (slide.isInProgress() || doNotShowMissingThumbnailImage) {
|
||||
return request;
|
||||
} else {
|
||||
return request.apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture));
|
||||
}
|
||||
}
|
||||
|
||||
private RequestBuilder buildPlaceholderGlideRequest(@NonNull GlideRequests glideRequests, @NonNull Slide slide) {
|
||||
@@ -547,10 +554,10 @@ public class ThumbnailView extends FrameLayout {
|
||||
bitmap = bitmap.load(slide.getPlaceholderRes(getContext().getTheme()));
|
||||
}
|
||||
|
||||
return applySizing(bitmap.diskCacheStrategy(DiskCacheStrategy.NONE), new CenterCrop());
|
||||
return applySizing(bitmap.diskCacheStrategy(DiskCacheStrategy.NONE));
|
||||
}
|
||||
|
||||
private GlideRequest applySizing(@NonNull GlideRequest request, @NonNull BitmapTransformation fitting) {
|
||||
private GlideRequest applySizing(@NonNull GlideRequest request) {
|
||||
int[] size = new int[2];
|
||||
fillTargetDimensions(size, dimens, bounds);
|
||||
if (size[WIDTH] == 0 && size[HEIGHT] == 0) {
|
||||
@@ -558,13 +565,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
size[HEIGHT] = getDefaultHeight();
|
||||
}
|
||||
|
||||
request = request.override(size[WIDTH], size[HEIGHT]);
|
||||
|
||||
if (radius > 0) {
|
||||
return request.transforms(fitting, new RoundedCorners(radius));
|
||||
} else {
|
||||
return request.transforms(fitting);
|
||||
}
|
||||
return request.override(size[WIDTH], size[HEIGHT]);
|
||||
}
|
||||
|
||||
private int getDefaultWidth() {
|
||||
@@ -585,6 +586,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
|
||||
public interface ThumbnailRequestListener extends RequestListener<Drawable> {
|
||||
void onLoadCanceled();
|
||||
|
||||
void onLoadScheduled();
|
||||
}
|
||||
|
||||
@@ -639,4 +641,22 @@ public class ThumbnailView extends FrameLayout {
|
||||
blurhash.setImageDrawable(null);
|
||||
}
|
||||
}
|
||||
|
||||
public class BitmapCaptor extends GlideDrawableListeningTarget {
|
||||
|
||||
public BitmapCaptor(@NonNull ImageView view, @NonNull SettableFuture<Boolean> loaded) {
|
||||
super(view, loaded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
|
||||
imageBitmap = ((BitmapDrawable) resource).getBitmap();
|
||||
super.onResourceReady(resource, transition);
|
||||
}
|
||||
|
||||
@Override public void onLoadCleared(@Nullable Drawable placeholder) {
|
||||
imageBitmap = null;
|
||||
super.onLoadCleared(placeholder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user