Load thumbnails using an asynchronous Glide target.
This commit is contained in:
parent
85551ca824
commit
ae73601f52
3 changed files with 102 additions and 70 deletions
|
@ -17,7 +17,6 @@
|
|||
package org.thoughtcrime.securesms.conversationlist;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
|
@ -32,16 +31,13 @@ import androidx.annotation.ColorInt;
|
|||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.Transformations;
|
||||
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
|
||||
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy;
|
||||
import com.makeramen.roundedimageview.RoundedDrawable;
|
||||
|
||||
import org.signal.core.util.DimensionUnit;
|
||||
|
@ -64,6 +60,7 @@ import org.thoughtcrime.securesms.database.model.LiveUpdateMessage;
|
|||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.database.model.UpdateDescription;
|
||||
import org.thoughtcrime.securesms.glide.GlideLiveDataTarget;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||
|
@ -82,9 +79,6 @@ import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
|||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import static org.thoughtcrime.securesms.database.model.LiveUpdateMessage.recipientToStringAsync;
|
||||
|
||||
|
@ -519,17 +513,16 @@ public final class ConversationListItem extends ConstraintLayout
|
|||
} else if (extra != null && extra.isRemoteDelete()) {
|
||||
return emphasisAdded(context, context.getString(thread.isOutgoing() ? R.string.ThreadRecord_you_deleted_this_message : R.string.ThreadRecord_this_message_was_deleted), defaultTint);
|
||||
} else {
|
||||
String body = removeNewlines(thread.getBody());
|
||||
|
||||
LiveData<SpannableString> finalBody = recipientToStringAsync(thread.getRecipient().getId(), threadRecipient -> {
|
||||
CharSequence bodyWithMediaIcon = createFinalBodyWithMediaIcon(context, body, thread, glideRequests);
|
||||
if (threadRecipient.isGroup()) {
|
||||
String body = removeNewlines(thread.getBody());
|
||||
LiveData<SpannableString> finalBody = LiveDataUtil.mapAsync(createFinalBodyWithMediaIcon(context, body, thread, glideRequests), updatedBody -> {
|
||||
if (thread.getRecipient().isGroup()) {
|
||||
RecipientId groupMessageSender = thread.getGroupMessageSender();
|
||||
if (!groupMessageSender.isUnknown()) {
|
||||
return createGroupMessageUpdateString(context, bodyWithMediaIcon, Recipient.resolved(groupMessageSender), thread.isRead());
|
||||
return createGroupMessageUpdateString(context, updatedBody, Recipient.resolved(groupMessageSender), thread.isRead());
|
||||
}
|
||||
}
|
||||
return new SpannableString(bodyWithMediaIcon);
|
||||
|
||||
return new SpannableString(updatedBody);
|
||||
});
|
||||
|
||||
return whileLoadingShow(body, finalBody);
|
||||
|
@ -537,63 +530,57 @@ public final class ConversationListItem extends ConstraintLayout
|
|||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private static CharSequence createFinalBodyWithMediaIcon(@NonNull Context context,
|
||||
@NonNull String body,
|
||||
@NonNull ThreadRecord thread,
|
||||
@NonNull GlideRequests glideRequests)
|
||||
private static LiveData<CharSequence> createFinalBodyWithMediaIcon(@NonNull Context context,
|
||||
@NonNull String body,
|
||||
@NonNull ThreadRecord thread,
|
||||
@NonNull GlideRequests glideRequests)
|
||||
{
|
||||
if (thread.getSnippetUri() != null) {
|
||||
try {
|
||||
int thumbSize = (int) DimensionUnit.SP.toPixels(20f);
|
||||
Bitmap thumb = glideRequests.asBitmap()
|
||||
.load(new DecryptableStreamUriLoader.DecryptableUri(thread.getSnippetUri()))
|
||||
.override(thumbSize, thumbSize)
|
||||
.transform(
|
||||
new OverlayTransformation(ContextCompat.getColor(context, R.color.transparent_black_08)),
|
||||
new CenterCrop()
|
||||
)
|
||||
.submit()
|
||||
.get(1, TimeUnit.SECONDS);
|
||||
|
||||
RoundedDrawable drawable = RoundedDrawable.fromBitmap(thumb);
|
||||
drawable.setBounds(0, 0, thumbSize, thumbSize);
|
||||
drawable.setCornerRadius(DimensionUnit.DP.toPixels(4));
|
||||
drawable.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
|
||||
|
||||
CharSequence span = SpanUtil.buildCenteredImageSpan(drawable);
|
||||
|
||||
final String withoutPrefix;
|
||||
|
||||
if (body.startsWith(EmojiStrings.GIF)) {
|
||||
withoutPrefix = body.replace(EmojiStrings.GIF, "");
|
||||
} else if (body.startsWith(EmojiStrings.VIDEO)) {
|
||||
withoutPrefix = body.replace(EmojiStrings.VIDEO, "");
|
||||
} else if (body.startsWith(EmojiStrings.PHOTO)) {
|
||||
withoutPrefix = body.replace(EmojiStrings.PHOTO, "");
|
||||
} else if (thread.getExtra() != null && thread.getExtra().getStickerEmoji() != null && body.startsWith(thread.getExtra().getStickerEmoji())) {
|
||||
withoutPrefix = body.replace(thread.getExtra().getStickerEmoji(), "");
|
||||
} else {
|
||||
withoutPrefix = null;
|
||||
}
|
||||
|
||||
if (withoutPrefix != null) {
|
||||
return new SpannableStringBuilder()
|
||||
.append(span)
|
||||
.append(withoutPrefix);
|
||||
} else {
|
||||
return body;
|
||||
}
|
||||
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
return new SpannableString(body);
|
||||
} catch (TimeoutException e) {
|
||||
Log.w(TAG, "Hit a timeout when generating a thumbnail! " + thread.getSnippetUri());
|
||||
return new SpannableString(body);
|
||||
}
|
||||
} else {
|
||||
return new SpannableString(body);
|
||||
if (thread.getSnippetUri() == null) {
|
||||
return LiveDataUtil.just(body);
|
||||
}
|
||||
|
||||
final String bodyWithoutMediaPrefix;
|
||||
|
||||
if (body.startsWith(EmojiStrings.GIF)) {
|
||||
bodyWithoutMediaPrefix = body.replace(EmojiStrings.GIF, "");
|
||||
} else if (body.startsWith(EmojiStrings.VIDEO)) {
|
||||
bodyWithoutMediaPrefix = body.replace(EmojiStrings.VIDEO, "");
|
||||
} else if (body.startsWith(EmojiStrings.PHOTO)) {
|
||||
bodyWithoutMediaPrefix = body.replace(EmojiStrings.PHOTO, "");
|
||||
} else if (thread.getExtra() != null && thread.getExtra().getStickerEmoji() != null && body.startsWith(thread.getExtra().getStickerEmoji())) {
|
||||
bodyWithoutMediaPrefix = body.replace(thread.getExtra().getStickerEmoji(), "");
|
||||
} else {
|
||||
return LiveDataUtil.just(body);
|
||||
}
|
||||
|
||||
int thumbSize = (int) DimensionUnit.SP.toPixels(20f);
|
||||
GlideLiveDataTarget target = new GlideLiveDataTarget(thumbSize, thumbSize);
|
||||
|
||||
glideRequests.asBitmap()
|
||||
.load(new DecryptableStreamUriLoader.DecryptableUri(thread.getSnippetUri()))
|
||||
.override(thumbSize, thumbSize)
|
||||
.transform(
|
||||
new OverlayTransformation(ContextCompat.getColor(context, R.color.transparent_black_08)),
|
||||
new CenterCrop()
|
||||
)
|
||||
.into(target);
|
||||
|
||||
return Transformations.map(target.getLiveData(), bitmap -> {
|
||||
if (bitmap == null) {
|
||||
return body;
|
||||
}
|
||||
|
||||
RoundedDrawable drawable = RoundedDrawable.fromBitmap(bitmap);
|
||||
drawable.setBounds(0, 0, thumbSize, thumbSize);
|
||||
drawable.setCornerRadius(DimensionUnit.DP.toPixels(4));
|
||||
drawable.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
|
||||
|
||||
CharSequence thumbnailSpan = SpanUtil.buildCenteredImageSpan(drawable);
|
||||
|
||||
return new SpannableStringBuilder()
|
||||
.append(thumbnailSpan)
|
||||
.append(bodyWithoutMediaPrefix);
|
||||
});
|
||||
}
|
||||
|
||||
private static SpannableString createGroupMessageUpdateString(@NonNull Context context,
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package org.thoughtcrime.securesms.glide;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
|
||||
/**
|
||||
* A Glide target that exposes a LiveData<Bitmap> that can be observed.
|
||||
*
|
||||
* If the load is canceled or otherwise fails, it will post a null value.
|
||||
*/
|
||||
public class GlideLiveDataTarget extends CustomTarget<Bitmap> {
|
||||
|
||||
private final MutableLiveData<Bitmap> liveData = new MutableLiveData<>();
|
||||
|
||||
public GlideLiveDataTarget(int width, int height) {
|
||||
super(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
|
||||
liveData.postValue(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCleared(@Nullable Drawable placeholder) {
|
||||
liveData.postValue(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFailed(@Nullable Drawable errorDrawable) {
|
||||
liveData.postValue(null);
|
||||
}
|
||||
|
||||
public @NonNull LiveData<Bitmap> getLiveData() {
|
||||
return liveData;
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
public final class SignalExecutors {
|
||||
|
||||
public static final ExecutorService UNBOUNDED = Executors.newCachedThreadPool(new NumberedThreadFactory("signal-unbounded"));
|
||||
public static final ExecutorService BOUNDED = newCachedBoundedExecutor("signal-bounded", 4, 12, 5);
|
||||
public static final ExecutorService BOUNDED = Executors.newFixedThreadPool(4, new NumberedThreadFactory("signal-bounded"));
|
||||
public static final ExecutorService SERIAL = Executors.newSingleThreadExecutor(new NumberedThreadFactory("signal-serial"));
|
||||
public static final ExecutorService BOUNDED_IO = newCachedBoundedExecutor("signal-io-bounded", 1, 32, 30);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue