From d669e8a0b10544495bd064a2eaef0b720a587698 Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Fri, 6 Nov 2020 14:50:00 +1100 Subject: [PATCH] enable link preview for any site --- .../linkpreview/LinkPreviewRepository.java | 90 ++++++++++++------- 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/src/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java b/src/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java index fc931c0545..5401a992a1 100644 --- a/src/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java +++ b/src/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java @@ -2,13 +2,16 @@ package org.thoughtcrime.securesms.linkpreview; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.net.Uri; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.text.Html; import android.text.TextUtils; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.request.FutureTarget; +import com.google.android.gms.common.util.IOUtils; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.attachments.Attachment; @@ -37,6 +40,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifes import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.regex.Matcher; @@ -73,7 +77,7 @@ public class LinkPreviewRepository implements InjectableType { RequestController getLinkPreview(@NonNull Context context, @NonNull String url, @NonNull Callback> callback) { CompositeRequestController compositeController = new CompositeRequestController(); - if (!LinkPreviewUtil.isWhitelistedLinkUrl(url)) { + if (!LinkPreviewUtil.isValidLinkUrl(url)) { Log.w(TAG, "Tried to get a link preview for a non-whitelisted domain."); callback.onComplete(Optional.absent()); return compositeController; @@ -137,7 +141,7 @@ public class LinkPreviewRepository implements InjectableType { Optional title = getProperty(body, "title"); Optional imageUrl = getProperty(body, "image"); - if (imageUrl.isPresent() && !LinkPreviewUtil.isWhitelistedMediaUrl(imageUrl.get())) { + if (imageUrl.isPresent() && !LinkPreviewUtil.isValidMediaUrl(imageUrl.get())) { Log.i(TAG, "Image URL was invalid or for a non-whitelisted domain. Skipping."); imageUrl = Optional.absent(); } @@ -150,48 +154,34 @@ public class LinkPreviewRepository implements InjectableType { } private @NonNull RequestController fetchThumbnail(@NonNull Context context, @NonNull String imageUrl, @NonNull Callback> callback) { - FutureTarget bitmapFuture = GlideApp.with(context).asBitmap() - .load(new ChunkedImageUrl(imageUrl)) - .skipMemoryCache(true) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .centerInside() - .submit(1024, 1024); - - RequestController controller = () -> bitmapFuture.cancel(false); + Call call = client.newCall(new Request.Builder().url(imageUrl).build()); + CallRequestController controller = new CallRequestController(call); SignalExecutors.UNBOUNDED.execute(() -> { try { - Bitmap bitmap = bitmapFuture.get(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos); - - byte[] bytes = baos.toByteArray(); - Uri uri = BlobProvider.getInstance().forData(bytes).createForSingleSessionInMemory(); - Optional thumbnail = Optional.of(new UriAttachment(uri, - uri, - MediaUtil.IMAGE_JPEG, - AttachmentDatabase.TRANSFER_PROGRESS_STARTED, - bytes.length, - bitmap.getWidth(), - bitmap.getHeight(), - null, - null, - false, - false, - null, - null)); + Response response = call.execute(); + if (!response.isSuccessful() || response.body() == null) { + return; + } + + InputStream bodyStream = response.body().byteStream(); + controller.setStream(bodyStream); + + byte[] data = IOUtils.readInputStreamFully(bodyStream); + Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); + Optional thumbnail = bitmapToAttachment(bitmap, Bitmap.CompressFormat.JPEG, MediaUtil.IMAGE_JPEG); + + if (bitmap != null) bitmap.recycle(); callback.onComplete(thumbnail); - } catch (CancellationException | ExecutionException | InterruptedException e) { + } catch (IOException e) { + Log.w(TAG, "Exception during link preview image retrieval.", e); controller.cancel(); callback.onComplete(Optional.absent()); - } finally { - bitmapFuture.cancel(false); } }); - return () -> bitmapFuture.cancel(true); + return controller; } private @NonNull Optional getProperty(@NonNull String searchText, @NonNull String property) { @@ -266,6 +256,38 @@ public class LinkPreviewRepository implements InjectableType { return () -> Log.i(TAG, "Cancelled sticker pack link preview fetch -- no effect."); } + private static Optional bitmapToAttachment(@Nullable Bitmap bitmap, + @NonNull Bitmap.CompressFormat format, + @NonNull String contentType) + { + if (bitmap == null) { + return Optional.absent(); + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + bitmap.compress(format, 80, baos); + + byte[] bytes = baos.toByteArray(); + Uri uri = BlobProvider.getInstance().forData(bytes).createForSingleSessionInMemory(); + + return Optional.of(new UriAttachment(uri, + uri, + contentType, + AttachmentDatabase.TRANSFER_PROGRESS_STARTED, + bytes.length, + bitmap.getWidth(), + bitmap.getHeight(), + null, + null, + false, + false, + null, + null)); + + } + + private static class Metadata { private final Optional title; private final Optional imageUrl;