From 4f4a5bb2a925b0f4c9fd62187cf90a4651af7ff7 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Mon, 3 Oct 2022 13:54:52 -0500 Subject: [PATCH] Actually display images we already have inline in XHTML-IM If we already have the image for the Cid downloaded, then do create the thumbnail and show it inline. --- .../persistance/DatabaseBackend.java | 7 +-- .../persistance/FileBackend.java | 41 +++++++++------ .../services/XmppConnectionService.java | 3 +- .../ui/adapter/MessageAdapter.java | 50 ++++++++++++++++++- .../siacs/conversations/utils/UIHelper.java | 6 ++- 5 files changed, 86 insertions(+), 21 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index bf69e87613035244c94d62fdf3c827fa7690d924..4ab3b02d3c0824432fe027eac3848916789ff6a4 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -48,6 +48,7 @@ import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.PresenceTemplate; import eu.siacs.conversations.entities.Roster; @@ -733,12 +734,12 @@ public class DatabaseBackend extends SQLiteOpenHelper { cursor.close(); } - public File getFileForCid(Cid cid) { + public DownloadableFile getFileForCid(Cid cid) { SQLiteDatabase db = this.getReadableDatabase(); Cursor cursor = db.query("cheogram.cids", new String[]{"path"}, "cid=?", new String[]{cid.toString()}, null, null, null); - File f = null; + DownloadableFile f = null; if (cursor.moveToNext()) { - f = new File(cursor.getString(0)); + f = new DownloadableFile(cursor.getString(0)); } cursor.close(); return f; diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index c2edaa3a15da9b4155e162762c37283e99f8402d..13118252c08e574289c96c414e285bb18c0ccefd 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -13,6 +13,7 @@ import android.graphics.drawable.Drawable; import android.graphics.ImageDecoder; import android.graphics.Matrix; import android.graphics.Paint; +import android.graphics.Rect; import android.graphics.RectF; import android.graphics.pdf.PdfRenderer; import android.media.MediaMetadataRetriever; @@ -995,16 +996,18 @@ public class FileBackend { } public Drawable getThumbnail(Message message, Resources res, int size, boolean cacheOnly) throws IOException { - final String uuid = message.getUuid(); + return getThumbnail(getFile(message), res, size, cacheOnly); + } + + public Drawable getThumbnail(DownloadableFile file, Resources res, int size, boolean cacheOnly) throws IOException { final LruCache cache = mXmppConnectionService.getDrawableCache(); - Drawable thumbnail = cache.get(uuid); + Drawable thumbnail = cache.get(file.getAbsolutePath()); if ((thumbnail == null) && (!cacheOnly)) { synchronized (THUMBNAIL_LOCK) { - thumbnail = cache.get(uuid); + thumbnail = cache.get(file.getAbsolutePath()); if (thumbnail != null) { return thumbnail; } - DownloadableFile file = getFile(message); final String mime = file.getMimeType(); if ("application/pdf".equals(mime)) { thumbnail = new BitmapDrawable(res, getPdfDocumentPreview(file, size)); @@ -1016,7 +1019,7 @@ public class FileBackend { throw new FileNotFoundException(); } } - cache.put(uuid, thumbnail); + cache.put(file.getAbsolutePath(), thumbnail); } } return thumbnail; @@ -1028,22 +1031,30 @@ public class FileBackend { return drawDrawable(drawable); } + public static Rect rectForSize(int w, int h, int size) { + int scalledW; + int scalledH; + if (w <= h) { + scalledW = Math.max((int) (w / ((double) h / size)), 1); + scalledH = size; + } else { + scalledW = size; + scalledH = Math.max((int) (h / ((double) w / size)), 1); + } + + if (scalledW > w || scalledH > h) return new Rect(0, 0, w, h); + + return new Rect(0, 0, scalledW, scalledH); + } + private Drawable getImagePreview(File file, Resources res, int size, final String mime) throws IOException { if (android.os.Build.VERSION.SDK_INT >= 28) { ImageDecoder.Source source = ImageDecoder.createSource(file); return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> { int w = info.getSize().getWidth(); int h = info.getSize().getHeight(); - int scalledW; - int scalledH; - if (w <= h) { - scalledW = Math.max((int) (w / ((double) h / size)), 1); - scalledH = size; - } else { - scalledW = size; - scalledH = Math.max((int) (h / ((double) w / size)), 1); - } - decoder.setTargetSize(scalledW, scalledH); + Rect r = rectForSize(w, h, size); + decoder.setTargetSize(r.width(), r.height()); }); } else { BitmapFactory.Options options = new BitmapFactory.Options(); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index f8ca42db270c5916f52bcdd59c56c8a2eec4449d..3f4838eb5d6058beee104a08bdff8df9fbd8b293 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -103,6 +103,7 @@ import eu.siacs.conversations.entities.Bookmark; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversational; +import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions.OnRenameListener; @@ -549,7 +550,7 @@ public class XmppConnectionService extends Service { return this.fileBackend; } - public File getFileForCid(Cid cid) { + public DownloadableFile getFileForCid(Cid cid) { return this.databaseBackend.getFileForCid(cid); } diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index f56c2a8e66c588b914d5cfbf8341be011dc635a6..efb5e9b1373d6854036030c4ae172221e8e4753e 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -6,8 +6,10 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; import android.graphics.Typeface; import android.net.Uri; +import android.os.AsyncTask; import android.preference.PreferenceManager; import android.text.Spannable; import android.text.SpannableString; @@ -32,15 +34,19 @@ import android.widget.Toast; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import androidx.core.content.res.ResourcesCompat; import com.google.common.base.Strings; +import java.io.IOException; import java.net.URI; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; +import io.ipfs.cid.Cid; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.FingerprintStatus; @@ -439,7 +445,25 @@ public class MessageAdapter extends ArrayAdapter { if (message.getBody() != null && !message.getBody().equals("")) { viewHolder.messageBody.setVisibility(View.VISIBLE); final String nick = UIHelper.getMessageDisplayName(message); - SpannableStringBuilder body = message.getMergedBody(); + Drawable fallbackImg = ResourcesCompat.getDrawable(activity.getResources(), activity.getThemeResource(R.attr.ic_attach_photo, R.drawable.ic_attach_photo), null); + fallbackImg.setBounds(FileBackend.rectForSize(fallbackImg.getIntrinsicWidth(), fallbackImg.getIntrinsicHeight(), (int) (metrics.density * 32))); + SpannableStringBuilder body = message.getMergedBody((cid) -> { + try { + DownloadableFile f = activity.xmppConnectionService.getFileForCid(cid); + if (f == null) return null; + + Drawable d = activity.xmppConnectionService.getFileBackend().getThumbnail(f, activity.getResources(), (int) (metrics.density * 288), true); + if (d == null) { + new ThumbnailTask().execute(f); + } else { + d = d.getConstantState().newDrawable(); + d.setBounds(FileBackend.rectForSize(d.getIntrinsicWidth(), d.getIntrinsicHeight(), (int) (metrics.density * 32))); + } + return d; + } catch (final IOException e) { + return fallbackImg; + } + }, fallbackImg); boolean hasMeCommand = message.hasMeCommand(); if (hasMeCommand) { body = body.replace(0, Message.ME_COMMAND.length(), nick + " "); @@ -959,4 +983,28 @@ public class MessageAdapter extends ArrayAdapter { protected TextView encryption; protected ListView commands_list; } + + class ThumbnailTask extends AsyncTask { + @Override + protected Drawable[] doInBackground(DownloadableFile... params) { + if (isCancelled()) return null; + + Drawable[] d = new Drawable[params.length]; + for (int i = 0; i < params.length; i++) { + try { + d[i] = activity.xmppConnectionService.getFileBackend().getThumbnail(params[i], activity.getResources(), (int) (metrics.density * 288), false); + } catch (final IOException e) { + d[i] = null; + } + } + + return d; + } + + @Override + protected void onPostExecute(final Drawable[] d) { + if (isCancelled()) return; + activity.xmppConnectionService.updateConversationUi(); + } + } } diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java index 578bb2c89af778fcdce6318832222aedc941f2dc..7692138be38c01d2268ee43e7626cdae00719e86 100644 --- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java @@ -1,12 +1,14 @@ package eu.siacs.conversations.utils; import android.content.Context; +import android.graphics.drawable.Drawable; import android.text.SpannableStringBuilder; import android.text.format.DateFormat; import android.text.format.DateUtils; import android.util.Pair; import androidx.annotation.ColorInt; +import androidx.core.content.res.ResourcesCompat; import com.google.common.base.Strings; @@ -319,7 +321,9 @@ public class UIHelper { return new Pair<>(context.getString(R.string.x_file_offered_for_download, getFileDescriptionString(context, message)), true); } else { - SpannableStringBuilder styledBody = new SpannableStringBuilder(body); + Drawable fallbackImg = ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_attach_photo, null); + fallbackImg.setBounds(0, 0, fallbackImg.getIntrinsicWidth(), fallbackImg.getIntrinsicHeight()); + SpannableStringBuilder styledBody = message.getSpannableBody(null, fallbackImg); if (textColor != 0) { StylingHelper.format(styledBody, 0, styledBody.length() - 1, textColor); }