diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 7c1287bc1..ee2b2d636 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -133,7 +133,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, attachment.getSize()); intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, attachment.getCaption()); intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, leftIsRecent); - intent.setDataAndType(attachment.getDataUri(), mediaRecord.getContentType()); + intent.setDataAndType(attachment.getUri(), mediaRecord.getContentType()); return intent; } @@ -801,7 +801,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity return new MediaItem(Recipient.live(recipientId).get(), Recipient.live(threadRecipientId).get(), attachment, - Objects.requireNonNull(attachment.getDataUri()), + Objects.requireNonNull(attachment.getUri()), mediaRecord.getContentType(), mediaRecord.getDate(), mediaRecord.isOutgoing()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.java index 0ae74bdba..90696f2ce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.java @@ -106,10 +106,7 @@ public abstract class Attachment { } @Nullable - public abstract Uri getDataUri(); - - @Nullable - public abstract Uri getThumbnailUri(); + public abstract Uri getUri(); public int getTransferState() { return transferState; diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java index f294b9d98..023211642 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java @@ -57,7 +57,7 @@ public class DatabaseAttachment extends Attachment { @Override @Nullable - public Uri getDataUri() { + public Uri getUri() { if (hasData) { return PartAuthority.getAttachmentDataUri(attachmentId); } else { @@ -65,16 +65,6 @@ public class DatabaseAttachment extends Attachment { } } - @Override - @Nullable - public Uri getThumbnailUri() { - if (hasThumbnail) { - return PartAuthority.getAttachmentThumbnailUri(attachmentId); - } else { - return null; - } - } - public AttachmentId getAttachmentId() { return attachmentId; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java index edcad15d9..3eda94374 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java @@ -15,13 +15,7 @@ public class MmsNotificationAttachment extends Attachment { @Nullable @Override - public Uri getDataUri() { - return null; - } - - @Nullable - @Override - public Uri getThumbnailUri() { + public Uri getUri() { return null; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.java index 6aeb5f7a4..3454922c7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.java @@ -42,17 +42,10 @@ public class PointerAttachment extends Attachment { @Nullable @Override - public Uri getDataUri() { + public Uri getUri() { return null; } - @Nullable - @Override - public Uri getThumbnailUri() { - return null; - } - - public static List forPointers(Optional> pointers) { List results = new LinkedList<>(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.java index 4b1af3ec9..4cdee54b8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.java @@ -20,12 +20,7 @@ public class TombstoneAttachment extends Attachment { } @Override - public @Nullable Uri getDataUri() { - return null; - } - - @Override - public @Nullable Uri getThumbnailUri() { + public @Nullable Uri getUri() { return null; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.java index 15dbff49c..cc28d760d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.java @@ -13,7 +13,6 @@ import org.thoughtcrime.securesms.stickers.StickerLocator; public class UriAttachment extends Attachment { private final @NonNull Uri dataUri; - private final @Nullable Uri thumbnailUri; public UriAttachment(@NonNull Uri uri, @NonNull String contentType, @@ -29,11 +28,10 @@ public class UriAttachment extends Attachment { @Nullable AudioHash audioHash, @Nullable TransformProperties transformProperties) { - this(uri, uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, borderless, quote, caption, stickerLocator, blurHash, audioHash, transformProperties); + this(uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, borderless, quote, caption, stickerLocator, blurHash, audioHash, transformProperties); } public UriAttachment(@NonNull Uri dataUri, - @Nullable Uri thumbnailUri, @NonNull String contentType, int transferState, long size, @@ -51,22 +49,15 @@ public class UriAttachment extends Attachment { @Nullable TransformProperties transformProperties) { super(contentType, transferState, size, fileName, 0, null, null, null, null, fastPreflightId, voiceNote, borderless, width, height, quote, 0, caption, stickerLocator, blurHash, audioHash, transformProperties); - this.dataUri = dataUri; - this.thumbnailUri = thumbnailUri; + this.dataUri = dataUri; } @Override @NonNull - public Uri getDataUri() { + public Uri getUri() { return dataUri; } - @Override - @Nullable - public Uri getThumbnailUri() { - return thumbnailUri; - } - @Override public boolean equals(Object other) { return other != null && other instanceof UriAttachment && ((UriAttachment) other).dataUri.equals(this.dataUri); diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java index 34f416a81..8a3e616a1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java @@ -138,13 +138,11 @@ public class FullBackupImporter extends FullBackupBase { inputStream.readAttachmentTo(output.second, attachment.getLength()); contentValues.put(AttachmentDatabase.DATA, dataFile.getAbsolutePath()); - contentValues.put(AttachmentDatabase.THUMBNAIL, (String)null); contentValues.put(AttachmentDatabase.DATA_RANDOM, output.first); } catch (BadMacException e) { Log.w(TAG, "Bad MAC for attachment " + attachment.getAttachmentId() + "! Can't restore it.", e); dataFile.delete(); contentValues.put(AttachmentDatabase.DATA, (String) null); - contentValues.put(AttachmentDatabase.THUMBNAIL, (String) null); contentValues.put(AttachmentDatabase.DATA_RANDOM, (String) null); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/BorderlessImageView.java b/app/src/main/java/org/thoughtcrime/securesms/components/BorderlessImageView.java index 7b605f058..ad99bb115 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/BorderlessImageView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/BorderlessImageView.java @@ -54,7 +54,7 @@ public class BorderlessImageView extends FrameLayout { } public void setSlide(@NonNull GlideRequests glideRequests, @NonNull Slide slide) { - boolean showControls = slide.asAttachment().getDataUri() == null; + boolean showControls = slide.asAttachment().getUri() == null; if (slide.hasSticker()) { image.setFit(new CenterInside()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java b/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java index d2b89606f..d1fc5341d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java @@ -242,14 +242,14 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver { if (!viewOnceSlides.isEmpty()) { thumbnailView.setVisibility(GONE); attachmentContainerView.setVisibility(GONE); - } else if (!imageVideoSlides.isEmpty() && imageVideoSlides.get(0).getThumbnailUri() != null) { + } else if (!imageVideoSlides.isEmpty() && imageVideoSlides.get(0).getUri() != null) { thumbnailView.setVisibility(VISIBLE); attachmentContainerView.setVisibility(GONE); dismissView.setBackgroundResource(R.drawable.dismiss_background); if (imageVideoSlides.get(0).hasVideo()) { attachmentVideoOverlayView.setVisibility(VISIBLE); } - glideRequests.load(new DecryptableUri(imageVideoSlides.get(0).getThumbnailUri())) + glideRequests.load(new DecryptableUri(imageVideoSlides.get(0).getUri())) .centerCrop() .override(getContext().getResources().getDimensionPixelSize(R.dimen.quote_thumb_size)) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/SharedContactView.java b/app/src/main/java/org/thoughtcrime/securesms/components/SharedContactView.java index 994f2c6b4..af89524e6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/SharedContactView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/SharedContactView.java @@ -119,7 +119,7 @@ public class SharedContactView extends LinearLayout implements RecipientForeverO this.activeRecipients.clear(); presentContact(contact); - presentAvatar(contact.getAvatarAttachment() != null ? contact.getAvatarAttachment().getDataUri() : null); + presentAvatar(contact.getAvatarAttachment() != null ? contact.getAvatarAttachment().getUri() : null); presentActionButtons(ContactUtil.getRecipients(getContext(), contact)); for (LiveRecipient recipient : activeRecipients.values()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java index b83793425..cb6f63598 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java @@ -279,7 +279,7 @@ public class ThumbnailView extends FrameLayout { getTransferControls().setVisibility(View.GONE); } - if (slide.getThumbnailUri() != null && slide.hasPlayOverlay() && + if (slide.getUri() != null && slide.hasPlayOverlay() && (slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE || isPreview)) { this.playOverlay.setVisibility(View.VISIBLE); @@ -288,12 +288,12 @@ public class ThumbnailView extends FrameLayout { } if (Util.equals(slide, this.slide)) { - Log.i(TAG, "Not re-loading slide " + slide.asAttachment().getDataUri()); + Log.i(TAG, "Not re-loading slide " + slide.asAttachment().getUri()); return new SettableFuture<>(false); } if (this.slide != null && this.slide.getFastPreflightId() != null && - (!slide.hasVideo() || Util.equals(this.slide.getThumbnailUri(), slide.getThumbnailUri())) && + (!slide.hasVideo() || Util.equals(this.slide.getUri(), slide.getUri())) && Util.equals(this.slide.getFastPreflightId(), slide.getFastPreflightId())) { Log.i(TAG, "Not re-loading slide for fast preflight: " + slide.getFastPreflightId()); @@ -301,7 +301,7 @@ public class ThumbnailView extends FrameLayout { return new SettableFuture<>(false); } - Log.i(TAG, "loading part with id " + slide.asAttachment().getDataUri() + Log.i(TAG, "loading part with id " + slide.asAttachment().getUri() + ", progress " + slide.getTransferState() + ", fast preflight id: " + slide.asAttachment().getFastPreflightId()); @@ -327,7 +327,7 @@ public class ThumbnailView extends FrameLayout { blurhash.setImageDrawable(null); } - if (slide.getThumbnailUri() != null) { + if (slide.getUri() != null) { if (!MediaUtil.isJpegType(slide.getContentType()) && !MediaUtil.isVideoType(slide.getContentType())) { SettableFuture thumbnailFuture = new SettableFuture<>(); thumbnailFuture.deferTo(result); @@ -412,7 +412,7 @@ public class ThumbnailView extends FrameLayout { } private GlideRequest buildThumbnailGlideRequest(@NonNull GlideRequests glideRequests, @NonNull Slide slide) { - GlideRequest request = applySizing(glideRequests.load(new DecryptableUri(slide.getThumbnailUri())) + GlideRequest request = applySizing(glideRequests.load(new DecryptableUri(slide.getUri())) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .transition(withCrossFade()), fit); @@ -469,10 +469,10 @@ public class ThumbnailView extends FrameLayout { private class ThumbnailClickDispatcher implements View.OnClickListener { @Override public void onClick(View view) { - if (thumbnailClickListener != null && - slide != null && - slide.asAttachment().getDataUri() != null && - slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE) + if (thumbnailClickListener != null && + slide != null && + slide.asAttachment().getUri() != null && + slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE) { thumbnailClickListener.onClick(view, slide); } else if (parentClickListener != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/contactshare/Contact.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/Contact.java index 54b06a096..2a9b970e2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contactshare/Contact.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contactshare/Contact.java @@ -14,7 +14,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.AttachmentId; import org.thoughtcrime.securesms.attachments.UriAttachment; -import org.thoughtcrime.securesms.blurhash.BlurHash; import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.util.JsonUtils; import org.thoughtcrime.securesms.util.MediaUtil; @@ -648,7 +647,7 @@ public class Contact implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(attachment != null ? attachment.getDataUri() : null, flags); + dest.writeParcelable(attachment != null ? attachment.getUri() : null, flags); dest.writeByte((byte) (isProfile ? 1 : 0)); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactFieldAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactFieldAdapter.java index 8805f7bfd..727392ee3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactFieldAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactFieldAdapter.java @@ -215,7 +215,7 @@ class ContactFieldAdapter extends RecyclerView.Adapter valuesArray = new ArrayList<>(1); valuesArray.add(values); diff --git a/app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactDetailsActivity.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactDetailsActivity.java index cbd66dd12..2d8298b22 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactDetailsActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactDetailsActivity.java @@ -96,7 +96,7 @@ public class SharedContactDetailsActivity extends PassphraseRequiredActivity { presentContact(contact); presentActionButtons(ContactUtil.getRecipients(this, contact)); - presentAvatar(contact.getAvatarAttachment() != null ? contact.getAvatarAttachment().getDataUri() : null); + presentAvatar(contact.getAvatarAttachment() != null ? contact.getAvatarAttachment().getUri() : null); for (LiveRecipient recipient : activeRecipients.values()) { recipient.observe(this, r -> presentActionButtons(Collections.singletonList(r.getId()))); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index df87d3367..2a4e27584 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -676,11 +676,11 @@ public class ConversationActivity extends PassphraseRequiredActivity for (Media mediaItem : result.getNonUploadedMedia()) { if (MediaUtil.isVideoType(mediaItem.getMimeType())) { - slideDeck.addSlide(new VideoSlide(this, mediaItem.getUri(), 0, mediaItem.getCaption().orNull(), mediaItem.getTransformProperties().orNull())); + slideDeck.addSlide(new VideoSlide(this, mediaItem.getUri(), mediaItem.getSize(), mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull(), mediaItem.getTransformProperties().orNull())); } else if (MediaUtil.isGif(mediaItem.getMimeType())) { - slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.isBorderless(), mediaItem.getCaption().orNull())); + slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), mediaItem.getSize(), mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.isBorderless(), mediaItem.getCaption().orNull())); } else if (MediaUtil.isImageType(mediaItem.getMimeType())) { - slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), mediaItem.getMimeType(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.isBorderless(), mediaItem.getCaption().orNull(), null)); + slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), mediaItem.getMimeType(), mediaItem.getSize(), mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.isBorderless(), mediaItem.getCaption().orNull(), null)); } else { Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.getMimeType() + "'. Skipping."); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 1723288e3..35f76c368 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -810,7 +810,7 @@ public class ConversationFragment extends LoggingFragment { .toList(); for (Attachment attachment : attachments) { - Uri uri = attachment.getDataUri() != null ? attachment.getDataUri() : attachment.getThumbnailUri(); + Uri uri = attachment.getUri(); if (uri != null) { mediaList.add(new Media(uri, diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java index f60e91285..daf8f71c3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java @@ -19,11 +19,8 @@ package org.thoughtcrime.securesms.database; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; -import android.graphics.Bitmap; import android.media.MediaDataSource; -import android.media.MediaMetadataRetriever; import android.net.Uri; -import android.os.Build; import android.text.TextUtils; import android.util.Pair; @@ -58,14 +55,10 @@ import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.stickers.StickerLocator; import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.BitmapDecodingException; -import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.CursorUtil; import org.thoughtcrime.securesms.util.FileUtils; import org.thoughtcrime.securesms.util.JsonUtils; -import org.thoughtcrime.securesms.util.MediaMetadataRetrieverUtil; import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.MediaUtil.ThumbnailData; import org.thoughtcrime.securesms.util.SetUtil; import org.thoughtcrime.securesms.util.StorageUtil; import org.thoughtcrime.securesms.util.Util; @@ -74,7 +67,6 @@ import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.internal.util.JsonUtil; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -90,9 +82,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; public class AttachmentDatabase extends Database { @@ -111,8 +100,6 @@ public class AttachmentDatabase extends Database { private static final String TRANSFER_FILE = "transfer_file"; public static final String SIZE = "data_size"; static final String FILE_NAME = "file_name"; - public static final String THUMBNAIL = "thumbnail"; - static final String THUMBNAIL_ASPECT_RATIO = "aspect_ratio"; public static final String UNIQUE_ID = "unique_id"; static final String DIGEST = "digest"; static final String VOICE_NOTE = "voice_note"; @@ -124,7 +111,6 @@ public class AttachmentDatabase extends Database { static final String STICKER_EMOJI = "sticker_emoji"; static final String FAST_PREFLIGHT_ID = "fast_preflight_id"; public static final String DATA_RANDOM = "data_random"; - private static final String THUMBNAIL_RANDOM = "thumbnail_random"; static final String WIDTH = "width"; static final String HEIGHT = "height"; static final String CAPTION = "caption"; @@ -149,11 +135,10 @@ public class AttachmentDatabase extends Database { private static final String[] PROJECTION = new String[] {ROW_ID, MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION, - CDN_NUMBER, CONTENT_LOCATION, DATA, THUMBNAIL, - TRANSFER_STATE, SIZE, FILE_NAME, THUMBNAIL, - THUMBNAIL_ASPECT_RATIO, UNIQUE_ID, DIGEST, + CDN_NUMBER, CONTENT_LOCATION, DATA, + TRANSFER_STATE, SIZE, FILE_NAME, UNIQUE_ID, DIGEST, FAST_PREFLIGHT_ID, VOICE_NOTE, BORDERLESS, QUOTE, DATA_RANDOM, - THUMBNAIL_RANDOM, WIDTH, HEIGHT, CAPTION, STICKER_PACK_ID, + WIDTH, HEIGHT, CAPTION, STICKER_PACK_ID, STICKER_PACK_KEY, STICKER_ID, STICKER_EMOJI, DATA_HASH, VISUAL_HASH, TRANSFORM_PROPERTIES, TRANSFER_FILE, DISPLAY_ORDER, UPLOAD_TIMESTAMP }; @@ -175,15 +160,12 @@ public class AttachmentDatabase extends Database { DATA + " TEXT, " + SIZE + " INTEGER, " + FILE_NAME + " TEXT, " + - THUMBNAIL + " TEXT, " + - THUMBNAIL_ASPECT_RATIO + " REAL, " + UNIQUE_ID + " INTEGER NOT NULL, " + DIGEST + " BLOB, " + FAST_PREFLIGHT_ID + " TEXT, " + VOICE_NOTE + " INTEGER DEFAULT 0, " + BORDERLESS + " INTEGER DEFAULT 0, " + DATA_RANDOM + " BLOB, " + - THUMBNAIL_RANDOM + " BLOB, " + QUOTE + " INTEGER DEFAULT 0, " + WIDTH + " INTEGER DEFAULT 0, " + HEIGHT + " INTEGER DEFAULT 0, " + @@ -208,10 +190,6 @@ public class AttachmentDatabase extends Database { "CREATE INDEX IF NOT EXISTS part_data_index ON " + TABLE_NAME + " (" + DATA + ");" }; - private static final long STANDARD_THUMB_TIME = 1000; - - private final ExecutorService thumbnailExecutor = Util.newSingleThreadedLifoExecutor(); - private final AttachmentSecret attachmentSecret; public AttachmentDatabase(Context context, SQLCipherOpenHelper databaseHelper, AttachmentSecret attachmentSecret) { @@ -228,29 +206,6 @@ public class AttachmentDatabase extends Database { else return dataStream; } - public @NonNull InputStream getThumbnailStream(@NonNull AttachmentId attachmentId) - throws IOException - { - Log.d(TAG, "getThumbnailStream(" + attachmentId + ")"); - InputStream dataStream = getDataStream(attachmentId, THUMBNAIL, 0); - - if (dataStream != null) { - return dataStream; - } - - try { - InputStream generatedStream = thumbnailExecutor.submit(new ThumbnailFetchCallable(attachmentId, STANDARD_THUMB_TIME)).get(); - - if (generatedStream == null) throw new FileNotFoundException("No thumbnail stream available: " + attachmentId); - else return generatedStream; - } catch (InterruptedException ie) { - throw new AssertionError("interrupted"); - } catch (ExecutionException ee) { - Log.w(TAG, ee); - throw new IOException(ee); - } - } - public boolean containsStickerPackId(@NonNull String stickerPackId) { String selection = STICKER_PACK_ID + " = ?"; String[] args = new String[] { stickerPackId }; @@ -365,12 +320,11 @@ public class AttachmentDatabase extends Database { Cursor cursor = null; try { - cursor = database.query(TABLE_NAME, new String[] {DATA, THUMBNAIL, CONTENT_TYPE, ROW_ID, UNIQUE_ID}, MMS_ID + " = ?", + cursor = database.query(TABLE_NAME, new String[] {DATA, CONTENT_TYPE, ROW_ID, UNIQUE_ID}, MMS_ID + " = ?", new String[] {mmsId+""}, null, null, null); while (cursor != null && cursor.moveToNext()) { deleteAttachmentOnDisk(cursor.getString(cursor.getColumnIndex(DATA)), - cursor.getString(cursor.getColumnIndex(THUMBNAIL)), cursor.getString(cursor.getColumnIndex(CONTENT_TYPE)), new AttachmentId(cursor.getLong(cursor.getColumnIndex(ROW_ID)), cursor.getLong(cursor.getColumnIndex(UNIQUE_ID)))); @@ -418,12 +372,11 @@ public class AttachmentDatabase extends Database { Cursor cursor = null; try { - cursor = database.query(TABLE_NAME, new String[] {DATA, THUMBNAIL, CONTENT_TYPE, ROW_ID, UNIQUE_ID}, MMS_ID + " = ?", + cursor = database.query(TABLE_NAME, new String[] {DATA, CONTENT_TYPE, ROW_ID, UNIQUE_ID}, MMS_ID + " = ?", new String[] {mmsId+""}, null, null, null); while (cursor != null && cursor.moveToNext()) { deleteAttachmentOnDisk(cursor.getString(cursor.getColumnIndex(DATA)), - cursor.getString(cursor.getColumnIndex(THUMBNAIL)), cursor.getString(cursor.getColumnIndex(CONTENT_TYPE)), new AttachmentId(cursor.getLong(cursor.getColumnIndex(ROW_ID)), cursor.getLong(cursor.getColumnIndex(UNIQUE_ID)))); @@ -437,8 +390,6 @@ public class AttachmentDatabase extends Database { values.put(DATA, (String) null); values.put(DATA_RANDOM, (byte[]) null); values.put(DATA_HASH, (String) null); - values.put(THUMBNAIL, (String) null); - values.put(THUMBNAIL_RANDOM, (byte[]) null); values.put(FILE_NAME, (String) null); values.put(CAPTION, (String) null); values.put(SIZE, 0); @@ -463,7 +414,7 @@ public class AttachmentDatabase extends Database { SQLiteDatabase database = databaseHelper.getWritableDatabase(); try (Cursor cursor = database.query(TABLE_NAME, - new String[]{DATA, THUMBNAIL, CONTENT_TYPE}, + new String[]{DATA, CONTENT_TYPE}, PART_ID_WHERE, id.toStrings(), null, @@ -475,11 +426,10 @@ public class AttachmentDatabase extends Database { return; } String data = cursor.getString(cursor.getColumnIndex(DATA)); - String thumbnail = cursor.getString(cursor.getColumnIndex(THUMBNAIL)); String contentType = cursor.getString(cursor.getColumnIndex(CONTENT_TYPE)); database.delete(TABLE_NAME, PART_ID_WHERE, id.toStrings()); - deleteAttachmentOnDisk(data, thumbnail, contentType, id); + deleteAttachmentOnDisk(data, contentType, id); notifyAttachmentListeners(); } } @@ -502,10 +452,9 @@ public class AttachmentDatabase extends Database { filesOnDisk.add(file.getAbsolutePath()); } - try (Cursor cursor = databaseHelper.getReadableDatabase().query(true, TABLE_NAME, new String[] { DATA, THUMBNAIL }, null, null, null, null, null, null)) { + try (Cursor cursor = databaseHelper.getReadableDatabase().query(true, TABLE_NAME, new String[] { DATA }, null, null, null, null, null, null)) { while (cursor != null && cursor.moveToNext()) { filesInDb.add(CursorUtil.requireString(cursor, DATA)); - filesInDb.add(CursorUtil.requireString(cursor, THUMBNAIL)); } } @@ -530,7 +479,6 @@ public class AttachmentDatabase extends Database { } private void deleteAttachmentOnDisk(@Nullable String data, - @Nullable String thumbnail, @Nullable String contentType, @NonNull AttachmentId attachmentId) { @@ -561,8 +509,6 @@ public class AttachmentDatabase extends Database { values.putNull(DATA); values.putNull(DATA_RANDOM); values.putNull(DATA_HASH); - values.putNull(THUMBNAIL); - values.putNull(THUMBNAIL_RANDOM); deletedCount += database.update(TABLE_NAME, values, PART_ID_WHERE, weakReference.toStrings()); } database.setTransactionSuccessful(); @@ -581,15 +527,7 @@ public class AttachmentDatabase extends Database { } } - if (!TextUtils.isEmpty(thumbnail)) { - if (new File(thumbnail).delete()) { - Log.i(TAG, "[deleteAttachmentOnDisk] Deleted thumbnail. " + data + " " + attachmentId); - } else { - Log.w(TAG, "[deleteAttachmentOnDisk] Failed to delete attachment. " + data + " " + attachmentId); - } - } - - if (MediaUtil.isImageType(contentType) || thumbnail != null) { + if (MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType)) { Glide.get(context).clearDiskCache(); } } @@ -623,22 +561,17 @@ public class AttachmentDatabase extends Database { SQLiteDatabase database = databaseHelper.getWritableDatabase(); ContentValues values = new ContentValues(); DataInfo oldInfo = getAttachmentDataFileInfo(attachmentId, DATA); - DataInfo dataInfo = setAttachmentData(inputStream, false, attachmentId); + DataInfo dataInfo = setAttachmentData(inputStream, attachmentId); File transferFile = getTransferFile(databaseHelper.getReadableDatabase(), attachmentId); if (oldInfo != null) { updateAttachmentDataHash(database, oldInfo.hash, dataInfo); } - if (placeholder != null && placeholder.isQuote() && !placeholder.getContentType().startsWith("image")) { - values.put(THUMBNAIL, dataInfo.file.getAbsolutePath()); - values.put(THUMBNAIL_RANDOM, dataInfo.random); - } else { - values.put(DATA, dataInfo.file.getAbsolutePath()); - values.put(SIZE, dataInfo.length); - values.put(DATA_RANDOM, dataInfo.random); - values.put(DATA_HASH, dataInfo.hash); - } + values.put(DATA, dataInfo.file.getAbsolutePath()); + values.put(SIZE, dataInfo.length); + values.put(DATA_RANDOM, dataInfo.random); + values.put(DATA_HASH, dataInfo.hash); String visualHashString = getVisualHashStringOrNull(placeholder); if (visualHashString != null) { @@ -662,8 +595,6 @@ public class AttachmentDatabase extends Database { //noinspection ResultOfMethodCallIgnored transferFile.delete(); } - - thumbnailExecutor.submit(new ThumbnailFetchCallable(attachmentId, STANDARD_THUMB_TIME)); } private static @Nullable String getVisualHashStringOrNull(@Nullable Attachment attachment) { @@ -846,7 +777,6 @@ public class AttachmentDatabase extends Database { DataInfo dataInfo = setAttachmentData(destination, mediaStream.getStream(), - false, databaseAttachment.getAttachmentId()); ContentValues contentValues = new ContentValues(); @@ -1055,16 +985,8 @@ public class AttachmentDatabase extends Database { SQLiteDatabase database = databaseHelper.getReadableDatabase(); Cursor cursor = null; - String randomColumn; - - switch (dataType) { - case DATA: randomColumn = DATA_RANDOM; break; - case THUMBNAIL: randomColumn = THUMBNAIL_RANDOM; break; - default:throw new AssertionError("Unknown data type: " + dataType); - } - try { - cursor = database.query(TABLE_NAME, new String[]{dataType, SIZE, randomColumn, DATA_HASH}, PART_ID_WHERE, attachmentId.toStrings(), + cursor = database.query(TABLE_NAME, new String[]{dataType, SIZE, DATA_RANDOM, DATA_HASH}, PART_ID_WHERE, attachmentId.toStrings(), null, null, null); if (cursor != null && cursor.moveToFirst()) { @@ -1074,7 +996,7 @@ public class AttachmentDatabase extends Database { return new DataInfo(new File(cursor.getString(cursor.getColumnIndexOrThrow(dataType))), cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)), - cursor.getBlob(cursor.getColumnIndexOrThrow(randomColumn)), + cursor.getBlob(cursor.getColumnIndexOrThrow(DATA_RANDOM)), cursor.getString(cursor.getColumnIndexOrThrow(DATA_HASH))); } else { return null; @@ -1087,26 +1009,24 @@ public class AttachmentDatabase extends Database { } private @NonNull DataInfo setAttachmentData(@NonNull Uri uri, - boolean isThumbnail, @Nullable AttachmentId attachmentId) throws MmsException { try { InputStream inputStream = PartAuthority.getAttachmentStream(context, uri); - return setAttachmentData(inputStream, isThumbnail, attachmentId); + return setAttachmentData(inputStream, attachmentId); } catch (IOException e) { throw new MmsException(e); } } private @NonNull DataInfo setAttachmentData(@NonNull InputStream in, - boolean isThumbnail, @Nullable AttachmentId attachmentId) throws MmsException { try { File dataFile = newFile(); - return setAttachmentData(dataFile, in, isThumbnail, attachmentId); + return setAttachmentData(dataFile, in, attachmentId); } catch (IOException e) { throw new MmsException(e); } @@ -1119,7 +1039,6 @@ public class AttachmentDatabase extends Database { private @NonNull DataInfo setAttachmentData(@NonNull File destination, @NonNull InputStream in, - boolean isThumbnail, @Nullable AttachmentId attachmentId) throws MmsException { @@ -1130,18 +1049,16 @@ public class AttachmentDatabase extends Database { long length = Util.copy(digestInputStream, out.second); String hash = Base64.encodeBytes(digestInputStream.getMessageDigest().digest()); - if (!isThumbnail) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - Optional sharedDataInfo = findDuplicateDataFileInfo(database, hash, attachmentId); - if (sharedDataInfo.isPresent()) { - Log.i(TAG, "[setAttachmentData] Duplicate data file found! " + sharedDataInfo.get().file.getAbsolutePath()); - if (!destination.equals(sharedDataInfo.get().file) && destination.delete()) { - Log.i(TAG, "[setAttachmentData] Deleted original file. " + destination); - } - return sharedDataInfo.get(); - } else { - Log.i(TAG, "[setAttachmentData] No matching attachment data found. " + destination.getAbsolutePath()); + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + Optional sharedDataInfo = findDuplicateDataFileInfo(database, hash, attachmentId); + if (sharedDataInfo.isPresent()) { + Log.i(TAG, "[setAttachmentData] Duplicate data file found! " + sharedDataInfo.get().file.getAbsolutePath()); + if (!destination.equals(sharedDataInfo.get().file) && destination.delete()) { + Log.i(TAG, "[setAttachmentData] Deleted original file. " + destination); } + return sharedDataInfo.get(); + } else { + Log.i(TAG, "[setAttachmentData] No matching attachment data found. " + destination.getAbsolutePath()); } return new DataInfo(destination, length, out.first, hash); @@ -1216,7 +1133,7 @@ public class AttachmentDatabase extends Database { result.add(new DatabaseAttachment(new AttachmentId(object.getLong(ROW_ID), object.getLong(UNIQUE_ID)), object.getLong(MMS_ID), !TextUtils.isEmpty(object.getString(DATA)), - !TextUtils.isEmpty(object.getString(THUMBNAIL)), + MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType), contentType, object.getInt(TRANSFER_STATE), object.getLong(SIZE), @@ -1254,7 +1171,7 @@ public class AttachmentDatabase extends Database { cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID))), cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)), !cursor.isNull(cursor.getColumnIndexOrThrow(DATA)), - !cursor.isNull(cursor.getColumnIndexOrThrow(THUMBNAIL)), + MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType), contentType, cursor.getInt(cursor.getColumnIndexOrThrow(TRANSFER_STATE)), cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)), @@ -1296,10 +1213,9 @@ public class AttachmentDatabase extends Database { SQLiteDatabase database = databaseHelper.getWritableDatabase(); DataInfo dataInfo = null; long uniqueId = System.currentTimeMillis(); - long thumbnailTimeUs; - if (attachment.getDataUri() != null) { - dataInfo = setAttachmentData(attachment.getDataUri(), false, null); + if (attachment.getUri() != null) { + dataInfo = setAttachmentData(attachment.getUri(), null); Log.d(TAG, "Wrote part to file: " + dataInfo.file.getAbsolutePath()); } @@ -1342,11 +1258,9 @@ public class AttachmentDatabase extends Database { if (attachment.getTransformProperties().isVideoEdited()) { contentValues.putNull(VISUAL_HASH); contentValues.put(TRANSFORM_PROPERTIES, attachment.getTransformProperties().serialize()); - thumbnailTimeUs = Math.max(STANDARD_THUMB_TIME, attachment.getTransformProperties().videoTrimStartTimeUs); } else { contentValues.put(VISUAL_HASH, getVisualHashStringOrNull(template)); contentValues.put(TRANSFORM_PROPERTIES, template.getTransformProperties().serialize()); - thumbnailTimeUs = STANDARD_THUMB_TIME; } if (attachment.isSticker()) { @@ -1370,38 +1284,6 @@ public class AttachmentDatabase extends Database { boolean notifyPacks = attachment.isSticker() && !hasStickerAttachments(); long rowId = database.insert(TABLE_NAME, null, contentValues); AttachmentId attachmentId = new AttachmentId(rowId, uniqueId); - Uri thumbnailUri = attachment.getThumbnailUri(); - boolean hasThumbnail = false; - - if (thumbnailUri != null) { - try (InputStream attachmentStream = PartAuthority.getAttachmentStream(context, thumbnailUri)) { - Pair dimens = BitmapUtil.getDimensions(attachmentStream); - updateAttachmentThumbnail(attachmentId, - PartAuthority.getAttachmentStream(context, thumbnailUri), - (float) dimens.first / (float) dimens.second); - hasThumbnail = true; - } catch (IOException | BitmapDecodingException e) { - Log.w(TAG, "Failed to save existing thumbnail.", e); - } - } - - if (!hasThumbnail && dataInfo != null) { - if (MediaUtil.hasVideoThumbnail(attachment.getDataUri()) && thumbnailTimeUs == STANDARD_THUMB_TIME) { - Bitmap bitmap = MediaUtil.getVideoThumbnail(context, attachment.getDataUri(), thumbnailTimeUs); - - if (bitmap != null) { - try (ThumbnailData thumbnailData = new ThumbnailData(bitmap)) { - updateAttachmentThumbnail(attachmentId, thumbnailData.toDataStream(), thumbnailData.getAspectRatio()); - } - } else { - Log.w(TAG, "Retrieving video thumbnail failed, submitting thumbnail generation job..."); - thumbnailExecutor.submit(new ThumbnailFetchCallable(attachmentId, thumbnailTimeUs)); - } - } else { - Log.i(TAG, "Submitting thumbnail generation job..."); - thumbnailExecutor.submit(new ThumbnailFetchCallable(attachmentId, thumbnailTimeUs)); - } - } if (notifyPacks) { notifyStickerPackListeners(); @@ -1423,35 +1305,6 @@ public class AttachmentDatabase extends Database { return null; } - @SuppressWarnings("WeakerAccess") - @VisibleForTesting - protected void updateAttachmentThumbnail(AttachmentId attachmentId, InputStream in, float aspectRatio) - throws MmsException - { - Log.i(TAG, "updating part thumbnail for #" + attachmentId); - - DataInfo thumbnailFile = setAttachmentData(in, true, attachmentId); - - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - ContentValues values = new ContentValues(2); - - values.put(THUMBNAIL, thumbnailFile.file.getAbsolutePath()); - values.put(THUMBNAIL_ASPECT_RATIO, aspectRatio); - values.put(THUMBNAIL_RANDOM, thumbnailFile.random); - - database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()); - - Cursor cursor = database.query(TABLE_NAME, new String[] {MMS_ID}, PART_ID_WHERE, attachmentId.toStrings(), null, null, null); - - try { - if (cursor != null && cursor.moveToFirst()) { - notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)))); - } - } finally { - if (cursor != null) cursor.close(); - } - } - @WorkerThread public void writeAudioHash(@NonNull AttachmentId attachmentId, @Nullable AudioWaveFormData audioWaveForm) { Log.i(TAG, "updating part audio wave form for #" + attachmentId); @@ -1468,66 +1321,6 @@ public class AttachmentDatabase extends Database { database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()); } - @VisibleForTesting - class ThumbnailFetchCallable implements Callable { - - private final AttachmentId attachmentId; - private final long timeUs; - - ThumbnailFetchCallable(AttachmentId attachmentId, long timeUs) { - this.attachmentId = attachmentId; - this.timeUs = timeUs; - } - - @Override - public @Nullable InputStream call() throws Exception { - Log.d(TAG, "Executing thumbnail job..."); - final InputStream stream = getDataStream(attachmentId, THUMBNAIL, 0); - - if (stream != null) { - return stream; - } - - DatabaseAttachment attachment = getAttachment(attachmentId); - - if (attachment == null || !attachment.hasData()) { - return null; - } - - if (MediaUtil.isVideoType(attachment.getContentType())) { - - try (ThumbnailData data = generateVideoThumbnail(attachmentId, timeUs)) { - - if (data != null) { - updateAttachmentThumbnail(attachmentId, data.toDataStream(), data.getAspectRatio()); - - return getDataStream(attachmentId, THUMBNAIL, 0); - } - } - } - - return null; - } - - private ThumbnailData generateVideoThumbnail(AttachmentId attachmentId, long timeUs) throws IOException { - if (Build.VERSION.SDK_INT < 23) { - Log.w(TAG, "Video thumbnails not supported..."); - return null; - } - - try (MediaDataSource dataSource = mediaDataSourceFor(attachmentId)) { - if (dataSource == null) return null; - - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - MediaMetadataRetrieverUtil.setDataSource(retriever, dataSource); - - Bitmap bitmap = retriever.getFrameAtTime(timeUs); - - Log.i(TAG, "Generated video thumbnail..."); - return bitmap != null ? new ThumbnailData(bitmap) : null; - } - } - } @RequiresApi(23) public @Nullable MediaDataSource mediaDataSourceFor(@NonNull AttachmentId attachmentId) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java index 3bc355628..57e4d114c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java @@ -23,14 +23,12 @@ public class MediaDatabase extends Database { private static final String BASE_MEDIA_QUERY = "SELECT " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + " AS " + AttachmentDatabase.ROW_ID + ", " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_TYPE + ", " - + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.THUMBNAIL_ASPECT_RATIO + ", " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + ", " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.TRANSFER_STATE + ", " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + ", " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FILE_NAME + ", " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DATA + ", " - + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.THUMBNAIL + ", " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CDN_NUMBER + ", " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_DISPOSITION + ", " diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java index 6274398d2..446ecbf1a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -213,7 +213,6 @@ public class MmsDatabase extends MessageDatabase { "'" + AttachmentDatabase.SIZE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + ", " + "'" + AttachmentDatabase.FILE_NAME + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FILE_NAME + ", " + "'" + AttachmentDatabase.DATA + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DATA + ", " + - "'" + AttachmentDatabase.THUMBNAIL + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.THUMBNAIL + ", " + "'" + AttachmentDatabase.CONTENT_TYPE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_TYPE + ", " + "'" + AttachmentDatabase.CDN_NUMBER + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CDN_NUMBER + ", " + "'" + AttachmentDatabase.CONTENT_LOCATION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", " + diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java index 03ef88f96..2ad3372fc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java @@ -411,7 +411,6 @@ public class MmsSmsDatabase extends Database { "'" + AttachmentDatabase.SIZE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + ", " + "'" + AttachmentDatabase.FILE_NAME + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FILE_NAME + ", " + "'" + AttachmentDatabase.DATA + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DATA + ", " + - "'" + AttachmentDatabase.THUMBNAIL + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.THUMBNAIL + ", " + "'" + AttachmentDatabase.CONTENT_TYPE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_TYPE + ", " + "'" + AttachmentDatabase.CDN_NUMBER + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CDN_NUMBER + ", " + "'" + AttachmentDatabase.CONTENT_LOCATION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", " + diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java index 4ace9efa3..5a03673fc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -1095,7 +1095,7 @@ public class ThreadDatabase extends Database { Slide thumbnail = Optional.fromNullable(slideDeck.getThumbnailSlide()).or(Optional.fromNullable(slideDeck.getStickerSlide())).orNull(); if (thumbnail != null && !((MmsMessageRecord) record).isViewOnce()) { - return thumbnail.getThumbnailUri(); + return thumbnail.getUri(); } return null; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 284c8bbd2..98bc81571 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -57,6 +57,7 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter; import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.CursorUtil; import org.thoughtcrime.securesms.util.FileUtils; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.SqlUtil; @@ -146,8 +147,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int UNKNOWN_STORAGE_FIELDS = 71; private static final int STICKER_CONTENT_TYPE = 72; private static final int STICKER_EMOJI_IN_NOTIFICATIONS = 73; + private static final int THUMBNAIL_CLEANUP = 74; - private static final int DATABASE_VERSION = 73; + private static final int DATABASE_VERSION = 74; private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -1023,6 +1025,30 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL("ALTER TABLE part ADD COLUMN sticker_emoji TEXT DEFAULT NULL"); } + if (oldVersion < THUMBNAIL_CLEANUP) { + int total = 0; + int deleted = 0; + + try (Cursor cursor = db.rawQuery("SELECT thumbnail FROM part WHERE thumbnail NOT NULL", null)) { + if (cursor != null) { + total = cursor.getCount(); + Log.w(TAG, "Found " + total + " thumbnails to delete."); + } + + while (cursor != null && cursor.moveToNext()) { + File file = new File(CursorUtil.requireString(cursor, "thumbnail")); + + if (file.delete()) { + deleted++; + } else { + Log.w(TAG, "Failed to delete file! " + file.getAbsolutePath()); + } + } + } + + Log.w(TAG, "Deleted " + deleted + "/" + total + " thumbnail files."); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCoder.java b/app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCoder.java index b751507ee..66d6538e1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCoder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCoder.java @@ -58,7 +58,7 @@ class EncryptedCoder { } } - InputStream createEncryptedInputStream(@NonNull byte[] masterKey, @NonNull File file) throws IOException { + CipherInputStream createEncryptedInputStream(@NonNull byte[] masterKey, @NonNull File file) throws IOException { try { Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(masterKey, "HmacSHA256")); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java index 95a1996fc..78f40c03a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java @@ -249,7 +249,7 @@ public final class AttachmentCompressionJob extends BaseJob { try { BitmapUtil.ScaleResult scaleResult = BitmapUtil.createScaledBytes(context, - new DecryptableStreamUriLoader.DecryptableUri(attachment.getDataUri()), + new DecryptableStreamUriLoader.DecryptableUri(attachment.getUri()), constraints); return new MediaStream(new ByteArrayInputStream(scaleResult.getBitmap()), diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java index ade4ad6ba..bcad898ff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java @@ -1,14 +1,11 @@ package org.thoughtcrime.securesms.jobs; import android.graphics.Bitmap; -import android.media.MediaDataSource; -import android.media.MediaMetadataRetriever; import android.os.Build; import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; import org.greenrobot.eventbus.EventBus; import org.thoughtcrime.securesms.R; @@ -28,8 +25,6 @@ import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.service.GenericForegroundService; import org.thoughtcrime.securesms.service.NotificationController; -import org.thoughtcrime.securesms.util.FeatureFlags; -import org.thoughtcrime.securesms.util.MediaMetadataRetrieverUtil; import org.thoughtcrime.securesms.util.MediaUtil; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceMessageSender; @@ -40,6 +35,7 @@ import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec; import java.io.IOException; import java.io.InputStream; +import java.util.Objects; import java.util.concurrent.TimeUnit; /** @@ -157,8 +153,8 @@ public final class AttachmentUploadJob extends BaseJob { private @NonNull SignalServiceAttachment getAttachmentFor(Attachment attachment, @Nullable NotificationController notification, @Nullable ResumableUploadSpec resumableUploadSpec) throws InvalidAttachmentException { try { - if (attachment.getDataUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!"); - InputStream is = PartAuthority.getAttachmentStream(context, attachment.getDataUri()); + if (attachment.getUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!"); + InputStream is = PartAuthority.getAttachmentStream(context, attachment.getUri()); SignalServiceAttachment.Builder builder = SignalServiceAttachment.newStreamBuilder() .withStream(is) .withContentType(attachment.getContentType()) @@ -193,43 +189,34 @@ public final class AttachmentUploadJob extends BaseJob { private @Nullable String getImageBlurHash(@NonNull Attachment attachment) throws IOException { if (attachment.getBlurHash() != null) return attachment.getBlurHash().getHash(); - if (attachment.getDataUri() == null) return null; + if (attachment.getUri() == null) return null; - return BlurHashEncoder.encode(PartAuthority.getAttachmentStream(context, attachment.getDataUri())); + return BlurHashEncoder.encode(PartAuthority.getAttachmentStream(context, attachment.getUri())); } private @Nullable String getVideoBlurHash(@NonNull Attachment attachment) throws IOException { - if (attachment.getThumbnailUri() != null) { - return BlurHashEncoder.encode(PartAuthority.getAttachmentStream(context, attachment.getThumbnailUri())); + if (attachment.getBlurHash() != null) { + return attachment.getBlurHash().getHash(); } - if (attachment.getBlurHash() != null) return attachment.getBlurHash().getHash(); - if (Build.VERSION.SDK_INT < 23) { Log.w(TAG, "Video thumbnails not supported..."); return null; } - try (MediaDataSource dataSource = DatabaseFactory.getAttachmentDatabase(context).mediaDataSourceFor(attachmentId)) { - if (dataSource == null) return null; + Bitmap bitmap = MediaUtil.getVideoThumbnail(context, Objects.requireNonNull(attachment.getUri()), 1000); - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - MediaMetadataRetrieverUtil.setDataSource(retriever, dataSource); + if (bitmap != null) { + Bitmap thumb = Bitmap.createScaledBitmap(bitmap, 100, 100, false); + bitmap.recycle(); - Bitmap bitmap = retriever.getFrameAtTime(1000); + Log.i(TAG, "Generated video thumbnail..."); + String hash = BlurHashEncoder.encode(thumb); + thumb.recycle(); - if (bitmap != null) { - Bitmap thumb = Bitmap.createScaledBitmap(bitmap, 100, 100, false); - bitmap.recycle(); - - Log.i(TAG, "Generated video thumbnail..."); - String hash = BlurHashEncoder.encode(thumb); - thumb.recycle(); - - return hash; - } else { - return null; - } + return hash; + } else { + return null; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MmsSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MmsSendJob.java index a4eb99fb1..2fabd66b6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MmsSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MmsSendJob.java @@ -27,7 +27,6 @@ import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.MessageDatabase; -import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; @@ -273,7 +272,7 @@ public final class MmsSendJob extends SendJob { for (Attachment attachment : scaledAttachments) { try { - if (attachment.getDataUri() == null) throw new IOException("Assertion failed, attachment for outgoing MMS has no data!"); + if (attachment.getUri() == null) throw new IOException("Assertion failed, attachment for outgoing MMS has no data!"); String fileName = attachment.getFileName(); PduPart part = new PduPart(); @@ -296,7 +295,7 @@ public final class MmsSendJob extends SendJob { int index = fileName.lastIndexOf("."); String contentId = (index == -1) ? fileName : fileName.substring(0, index); part.setContentId(contentId.getBytes()); - part.setData(Util.readFully(PartAuthority.getAttachmentStream(context, attachment.getDataUri()))); + part.setData(Util.readFully(PartAuthority.getAttachmentStream(context, attachment.getUri()))); body.addPart(part); size += getPartSize(part); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java index 3f6290085..c2e004213 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java @@ -1673,7 +1673,6 @@ public final class PushProcessMessageJob extends BaseJob { if (stickerRecord != null) { return Optional.of(new UriAttachment(stickerRecord.getUri(), - stickerRecord.getUri(), stickerRecord.getContentType(), AttachmentDatabase.TRANSFER_PROGRESS_DONE, stickerRecord.getSize(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java index d8b28e0be..06e78a484 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.jobs; import android.content.Context; import android.graphics.Bitmap; +import android.os.Build; import android.text.TextUtils; import androidx.annotation.NonNull; @@ -40,7 +41,6 @@ import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.BitmapDecodingException; import org.thoughtcrime.securesms.util.BitmapUtil; -import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.Hex; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -118,8 +118,8 @@ public abstract class PushSendJob extends SendJob { protected SignalServiceAttachment getAttachmentFor(Attachment attachment) { try { - if (attachment.getDataUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!"); - InputStream is = PartAuthority.getAttachmentStream(context, attachment.getDataUri()); + if (attachment.getUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!"); + InputStream is = PartAuthority.getAttachmentStream(context, attachment.getUri()); return SignalServiceAttachment.newStreamBuilder() .withStream(is) .withContentType(attachment.getContentType()) @@ -192,14 +192,26 @@ public abstract class PushSendJob extends SendJob { final SignalServiceAttachmentRemoteId remoteId = SignalServiceAttachmentRemoteId.from(attachment.getLocation()); final byte[] key = Base64.decode(attachment.getKey()); + int width = attachment.getWidth(); + int height = attachment.getHeight(); + + if ((width == 0 || height == 0) && MediaUtil.hasVideoThumbnail(context, attachment.getUri())) { + Bitmap thumbnail = MediaUtil.getVideoThumbnail(context, attachment.getUri(), 1000); + + if (thumbnail != null) { + width = thumbnail.getWidth(); + height = thumbnail.getHeight(); + } + } + return new SignalServiceAttachmentPointer(attachment.getCdnNumber(), remoteId, attachment.getContentType(), key, Optional.of(Util.toIntExact(attachment.getSize())), Optional.absent(), - attachment.getWidth(), - attachment.getHeight(), + width, + height, Optional.fromNullable(attachment.getDigest()), Optional.fromNullable(attachment.getFileName()), attachment.isVoiceNote(), @@ -240,13 +252,17 @@ public abstract class PushSendJob extends SendJob { String thumbnailType = MediaUtil.IMAGE_JPEG; try { - if (MediaUtil.isImageType(attachment.getContentType()) && attachment.getDataUri() != null) { + if (MediaUtil.isImageType(attachment.getContentType()) && attachment.getUri() != null) { Bitmap.CompressFormat format = BitmapUtil.getCompressFormatForContentType(attachment.getContentType()); - thumbnailData = BitmapUtil.createScaledBytes(context, new DecryptableStreamUriLoader.DecryptableUri(attachment.getDataUri()), 100, 100, 500 * 1024, format); + thumbnailData = BitmapUtil.createScaledBytes(context, new DecryptableStreamUriLoader.DecryptableUri(attachment.getUri()), 100, 100, 500 * 1024, format); thumbnailType = attachment.getContentType(); - } else if (MediaUtil.isVideoType(attachment.getContentType()) && attachment.getThumbnailUri() != null) { - thumbnailData = BitmapUtil.createScaledBytes(context, new DecryptableStreamUriLoader.DecryptableUri(attachment.getThumbnailUri()), 100, 100, 500 * 1024); + } else if (Build.VERSION.SDK_INT >= 23 && MediaUtil.isVideoType(attachment.getContentType()) && attachment.getUri() != null) { + Bitmap bitmap = MediaUtil.getVideoThumbnail(context, attachment.getUri(), 1000); + + if (bitmap != null) { + thumbnailData = BitmapUtil.createScaledBytes(context, bitmap, 100, 100, 500 * 1024); + } } if (thumbnailData != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java index 6c505fccb..136fb581a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java @@ -335,7 +335,6 @@ public class LinkPreviewRepository { Uri uri = BlobProvider.getInstance().forData(bytes).createForSingleSessionInMemory(); return Optional.of(new UriAttachment(uri, - uri, contentType, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, bytes.length, diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java index 23ed5769a..c32cada08 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java @@ -47,8 +47,8 @@ final class MediaActions { List attachments = new LinkedList<>(); for (MediaDatabase.MediaRecord mediaRecord : mediaRecords) { - if (mediaRecord.getAttachment().getDataUri() != null) { - attachments.add(new SaveAttachmentTask.Attachment(mediaRecord.getAttachment().getDataUri(), + if (mediaRecord.getAttachment().getUri() != null) { + attachments.add(new SaveAttachmentTask.Attachment(mediaRecord.getAttachment().getUri(), mediaRecord.getContentType(), mediaRecord.getDate(), mediaRecord.getAttachment().getFileName())); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java index 3c7e6371f..c07ef1d2a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java @@ -210,7 +210,7 @@ public final class MediaOverviewPageFragment extends Fragment } private void handleMediaPreviewClick(@NonNull MediaDatabase.MediaRecord mediaRecord) { - if (mediaRecord.getAttachment().getDataUri() == null) { + if (mediaRecord.getAttachment().getUri() == null) { return; } @@ -231,7 +231,7 @@ public final class MediaOverviewPageFragment extends Fragment intent.putExtra(MediaPreviewActivity.SHOW_THREAD_EXTRA, threadId == MediaDatabase.ALL_THREADS); intent.putExtra(MediaPreviewActivity.SORTING_EXTRA, sorting.ordinal()); - intent.setDataAndType(mediaRecord.getAttachment().getDataUri(), mediaRecord.getContentType()); + intent.setDataAndType(mediaRecord.getAttachment().getUri(), mediaRecord.getContentType()); context.startActivity(intent); } else { if (!MediaUtil.isAudio(attachment)) { @@ -241,7 +241,7 @@ public final class MediaOverviewPageFragment extends Fragment } private static void showFileExternally(@NonNull Context context, @NonNull MediaDatabase.MediaRecord mediaRecord) { - Uri uri = mediaRecord.getAttachment().getDataUri(); + Uri uri = mediaRecord.getAttachment().getUri(); Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewFragment.java index 16e213998..8962e7360 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewFragment.java @@ -29,7 +29,7 @@ public abstract class MediaPreviewFragment extends Fragment { protected Events events; public static MediaPreviewFragment newInstance(@NonNull Attachment attachment, boolean autoPlay) { - return newInstance(attachment.getDataUri(), attachment.getContentType(), attachment.getSize(), autoPlay); + return newInstance(attachment.getUri(), attachment.getContentType(), attachment.getSize(), autoPlay); } public static MediaPreviewFragment newInstance(@NonNull Uri dataUri, @NonNull String contentType, long size, boolean autoPlay) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java index d32a490c0..322542eb6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java @@ -98,8 +98,7 @@ public class MediaPreviewViewModel extends ViewModel { } private @Nullable Media toMedia(@NonNull MediaRecord mediaRecord) { - Uri uri = mediaRecord.getAttachment().getThumbnailUri() != null ? mediaRecord.getAttachment().getThumbnailUri() - : mediaRecord.getAttachment().getDataUri(); + Uri uri = mediaRecord.getAttachment().getUri(); if (uri == null) { return null; diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaUploadRepository.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaUploadRepository.java index 94e5c198f..5f87a7776 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaUploadRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaUploadRepository.java @@ -191,11 +191,11 @@ class MediaUploadRepository { public static @NonNull Attachment asAttachment(@NonNull Context context, @NonNull Media media) { if (MediaUtil.isVideoType(media.getMimeType())) { - return new VideoSlide(context, media.getUri(), 0, media.getCaption().orNull(), media.getTransformProperties().orNull()).asAttachment(); + return new VideoSlide(context, media.getUri(), media.getSize(), media.getWidth(), media.getHeight(), media.getCaption().orNull(), media.getTransformProperties().orNull()).asAttachment(); } else if (MediaUtil.isGif(media.getMimeType())) { - return new GifSlide(context, media.getUri(), 0, media.getWidth(), media.getHeight(), media.isBorderless(), media.getCaption().orNull()).asAttachment(); + return new GifSlide(context, media.getUri(), media.getSize(), media.getWidth(), media.getHeight(), media.isBorderless(), media.getCaption().orNull()).asAttachment(); } else if (MediaUtil.isImageType(media.getMimeType())) { - return new ImageSlide(context, media.getUri(), media.getMimeType(), 0, media.getWidth(), media.getHeight(), media.isBorderless(), media.getCaption().orNull(), null).asAttachment(); + return new ImageSlide(context, media.getUri(), media.getMimeType(), media.getSize(), media.getWidth(), media.getHeight(), media.isBorderless(), media.getCaption().orNull(), null).asAttachment(); } else if (MediaUtil.isTextType(media.getMimeType())) { return new TextSlide(context, media.getUri(), null, media.getSize()).asAttachment(); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java index 5f8ab8703..fd3393607 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -39,7 +39,7 @@ public class ApplicationMigrations { private static final int LEGACY_CANONICAL_VERSION = 455; - public static final int CURRENT_VERSION = 18; + public static final int CURRENT_VERSION = 19; private static final class Version { static final int LEGACY = 1; @@ -60,6 +60,7 @@ public class ApplicationMigrations { static final int VERSIONED_PROFILE = 16; static final int PIN_OPT_OUT = 17; static final int TRIM_SETTINGS = 18; + static final int THUMBNAIL_CLEANUP = 19; } /** @@ -247,6 +248,10 @@ public class ApplicationMigrations { jobs.put(Version.TRIM_SETTINGS, new TrimByLengthSettingsMigrationJob()); } + if (lastSeenVersion < Version.THUMBNAIL_CLEANUP) { + jobs.put(Version.THUMBNAIL_CLEANUP, new DatabaseMigrationJob()); + } + return jobs; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/AudioSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/AudioSlide.java index 70bc66934..29f83cfb2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/AudioSlide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/AudioSlide.java @@ -39,19 +39,13 @@ public class AudioSlide extends Slide { } public AudioSlide(Context context, Uri uri, long dataSize, String contentType, boolean voiceNote) { - super(context, new UriAttachment(uri, null, contentType, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, dataSize, 0, 0, null, null, voiceNote, false, false, null, null, null, null, null)); + super(context, new UriAttachment(uri, contentType, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, dataSize, 0, 0, null, null, voiceNote, false, false, null, null, null, null, null)); } public AudioSlide(Context context, Attachment attachment) { super(context, attachment); } - @Override - @Nullable - public Uri getThumbnailUri() { - return null; - } - @Override public boolean hasPlaceholder() { return true; diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java b/app/src/main/java/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java index 9559962c9..523044b9f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java @@ -29,7 +29,7 @@ class DecryptableStreamLocalUriFetcher extends StreamLocalUriFetcher { @Override protected InputStream loadResource(Uri uri, ContentResolver contentResolver) throws FileNotFoundException { - if (MediaUtil.hasVideoThumbnail(uri)) { + if (MediaUtil.hasVideoThumbnail(context, uri)) { Bitmap thumbnail = MediaUtil.getVideoThumbnail(context, uri, 1000); if (thumbnail != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/GifSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/GifSlide.java index b95d6973e..b142b28ba 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/GifSlide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/GifSlide.java @@ -26,11 +26,6 @@ public class GifSlide extends ImageSlide { this.borderless = borderless; } - @Override - public @Nullable Uri getThumbnailUri() { - return getUri(); - } - @Override public boolean isBorderless() { return borderless; diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java index a34fb7200..5313eefc6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java @@ -54,11 +54,6 @@ public class ImageSlide extends Slide { return 0; } - @Override - public @Nullable Uri getThumbnailUri() { - return getUri(); - } - @Override public boolean hasImage() { return true; diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java index 614ac7ba2..6583a44f5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java @@ -48,8 +48,8 @@ public abstract class MediaConstraints { public boolean isSatisfied(@NonNull Context context, @NonNull Attachment attachment) { try { - return (MediaUtil.isGif(attachment) && attachment.getSize() <= getGifMaxSize(context) && isWithinBounds(context, attachment.getDataUri())) || - (MediaUtil.isImage(attachment) && attachment.getSize() <= getImageMaxSize(context) && isWithinBounds(context, attachment.getDataUri())) || + return (MediaUtil.isGif(attachment) && attachment.getSize() <= getGifMaxSize(context) && isWithinBounds(context, attachment.getUri())) || + (MediaUtil.isImage(attachment) && attachment.getSize() <= getImageMaxSize(context) && isWithinBounds(context, attachment.getUri())) || (MediaUtil.isAudio(attachment) && attachment.getSize() <= getAudioMaxSize(context)) || (MediaUtil.isVideo(attachment) && attachment.getSize() <= getVideoMaxSize(context)) || (MediaUtil.isFile(attachment) && attachment.getSize() <= getDocumentMaxSize(context)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java b/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java index c4aebd5eb..11f57e8f6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java @@ -22,24 +22,20 @@ import java.io.InputStream; public class PartAuthority { private static final String PART_URI_STRING = "content://org.thoughtcrime.securesms/part"; - private static final String THUMB_URI_STRING = "content://org.thoughtcrime.securesms/thumb"; private static final String STICKER_URI_STRING = "content://org.thoughtcrime.securesms/sticker"; private static final Uri PART_CONTENT_URI = Uri.parse(PART_URI_STRING); - private static final Uri THUMB_CONTENT_URI = Uri.parse(THUMB_URI_STRING); private static final Uri STICKER_CONTENT_URI = Uri.parse(STICKER_URI_STRING); private static final int PART_ROW = 1; - private static final int THUMB_ROW = 2; - private static final int PERSISTENT_ROW = 3; - private static final int BLOB_ROW = 4; - private static final int STICKER_ROW = 5; + private static final int PERSISTENT_ROW = 2; + private static final int BLOB_ROW = 3; + private static final int STICKER_ROW = 4; private static final UriMatcher uriMatcher; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI("org.thoughtcrime.securesms", "part/*/#", PART_ROW); - uriMatcher.addURI("org.thoughtcrime.securesms", "thumb/*/#", THUMB_ROW); uriMatcher.addURI("org.thoughtcrime.securesms", "sticker/#", STICKER_ROW); uriMatcher.addURI(DeprecatedPersistentBlobProvider.AUTHORITY, DeprecatedPersistentBlobProvider.EXPECTED_PATH_OLD, PERSISTENT_ROW); uriMatcher.addURI(DeprecatedPersistentBlobProvider.AUTHORITY, DeprecatedPersistentBlobProvider.EXPECTED_PATH_NEW, PERSISTENT_ROW); @@ -49,13 +45,6 @@ public class PartAuthority { public static InputStream getAttachmentThumbnailStream(@NonNull Context context, @NonNull Uri uri) throws IOException { - String contentType = getAttachmentContentType(context, uri); - int match = uriMatcher.match(uri); - - if (match == PART_ROW && MediaUtil.isVideoType(contentType)) { - return DatabaseFactory.getAttachmentDatabase(context).getThumbnailStream(new PartUriParser(uri).getPartId()); - } - return getAttachmentStream(context, uri); } @@ -66,7 +55,6 @@ public class PartAuthority { try { switch (match) { case PART_ROW: return DatabaseFactory.getAttachmentDatabase(context).getAttachmentStream(new PartUriParser(uri).getPartId(), 0); - case THUMB_ROW: return DatabaseFactory.getAttachmentDatabase(context).getThumbnailStream(new PartUriParser(uri).getPartId()); case STICKER_ROW: return DatabaseFactory.getStickerDatabase(context).getStickerStream(ContentUris.parseId(uri)); case PERSISTENT_ROW: return DeprecatedPersistentBlobProvider.getInstance(context).getStream(context, ContentUris.parseId(uri)); case BLOB_ROW: return BlobProvider.getInstance().getStream(context, uri); @@ -81,7 +69,6 @@ public class PartAuthority { int match = uriMatcher.match(uri); switch (match) { - case THUMB_ROW: case PART_ROW: Attachment attachment = DatabaseFactory.getAttachmentDatabase(context).getAttachment(new PartUriParser(uri).getPartId()); @@ -100,7 +87,6 @@ public class PartAuthority { int match = uriMatcher.match(uri); switch (match) { - case THUMB_ROW: case PART_ROW: Attachment attachment = DatabaseFactory.getAttachmentDatabase(context).getAttachment(new PartUriParser(uri).getPartId()); @@ -119,7 +105,6 @@ public class PartAuthority { int match = uriMatcher.match(uri); switch (match) { - case THUMB_ROW: case PART_ROW: Attachment attachment = DatabaseFactory.getAttachmentDatabase(context).getAttachment(new PartUriParser(uri).getPartId()); @@ -145,8 +130,7 @@ public class PartAuthority { } public static Uri getAttachmentThumbnailUri(AttachmentId attachmentId) { - Uri uri = Uri.withAppendedPath(THUMB_CONTENT_URI, String.valueOf(attachmentId.getUniqueId())); - return ContentUris.withAppendedId(uri, attachmentId.getRowId()); + return getAttachmentDataUri(attachmentId); } public static Uri getStickerUri(long id) { @@ -157,11 +141,18 @@ public class PartAuthority { int match = uriMatcher.match(uri); switch (match) { case PART_ROW: - case THUMB_ROW: case PERSISTENT_ROW: case BLOB_ROW: return true; } return false; } + + public static boolean isAttachmentUri(@NonNull Uri uri) { + return uriMatcher.match(uri) == PART_ROW; + } + + public static @NonNull AttachmentId requireAttachmentId(@NonNull Uri uri) { + return new PartUriParser(uri).getPartId(); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java index 393d55524..4cbc39da4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java @@ -52,12 +52,7 @@ public abstract class Slide { @Nullable public Uri getUri() { - return attachment.getDataUri(); - } - - @Nullable - public Uri getThumbnailUri() { - return attachment.getThumbnailUri(); + return attachment.getUri(); } @NonNull @@ -188,7 +183,6 @@ public abstract class Slide { String resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri)).or(defaultMime); String fastPreflightId = String.valueOf(new SecureRandom().nextLong()); return new UriAttachment(uri, - hasThumbnail ? uri : null, resolvedType, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, size, @@ -246,13 +240,12 @@ public abstract class Slide { this.hasImage() == that.hasImage() && this.hasVideo() == that.hasVideo() && this.getTransferState() == that.getTransferState() && - Util.equals(this.getUri(), that.getUri()) && - Util.equals(this.getThumbnailUri(), that.getThumbnailUri()); + Util.equals(this.getUri(), that.getUri()); } @Override public int hashCode() { return Util.hashCode(getContentType(), hasAudio(), hasImage(), - hasVideo(), getUri(), getThumbnailUri(), getTransferState()); + hasVideo(), getUri(), getTransferState()); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/StickerSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/StickerSlide.java index f4920301a..66b5de1bc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/StickerSlide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/StickerSlide.java @@ -37,11 +37,6 @@ public class StickerSlide extends Slide { return 0; } - @Override - public @Nullable Uri getThumbnailUri() { - return getUri(); - } - @Override public boolean hasSticker() { return true; diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/VideoSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/VideoSlide.java index 77cc7aad4..5fd0a9b48 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/VideoSlide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/VideoSlide.java @@ -37,7 +37,11 @@ public class VideoSlide extends Slide { } public VideoSlide(Context context, Uri uri, long dataSize, @Nullable String caption, @Nullable AttachmentDatabase.TransformProperties transformProperties) { - super(context, constructAttachmentFromUri(context, uri, MediaUtil.VIDEO_UNSPECIFIED, dataSize, 0, 0, MediaUtil.hasVideoThumbnail(uri), null, caption, null, null, null, false, false, false, transformProperties)); + super(context, constructAttachmentFromUri(context, uri, MediaUtil.VIDEO_UNSPECIFIED, dataSize, 0, 0, MediaUtil.hasVideoThumbnail(context, uri), null, caption, null, null, null, false, false, false, transformProperties)); + } + + public VideoSlide(Context context, Uri uri, long dataSize, int width, int height, @Nullable String caption, @Nullable AttachmentDatabase.TransformProperties transformProperties) { + super(context, constructAttachmentFromUri(context, uri, MediaUtil.VIDEO_UNSPECIFIED, dataSize, width, height, MediaUtil.hasVideoThumbnail(context, uri), null, caption, null, null, null, false, false, false, transformProperties)); } public VideoSlide(Context context, Attachment attachment) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java index 765103ee5..82a27347f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java @@ -344,8 +344,8 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil } private static Optional getThumbnailUri(@Nullable Slide slide) { - if (slide != null && !slide.isInProgress() && slide.getThumbnailUri() != null) { - return Optional.of(slide.getThumbnailUri()); + if (slide != null && !slide.isInProgress() && slide.getUri() != null) { + return Optional.of(slide.getUri()); } else { return Optional.absent(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java index 839e09767..f7847567b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java @@ -15,12 +15,15 @@ import android.webkit.MimeTypeMap; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.annotation.WorkerThread; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.resource.gif.GifDrawable; import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.AttachmentId; +import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.mms.AudioSlide; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; @@ -152,7 +155,7 @@ public class MediaUtil { @WorkerThread public static Pair getDimensions(@NonNull Context context, @Nullable String contentType, @Nullable Uri uri) { - if (uri == null || !MediaUtil.isImageType(contentType)) { + if (uri == null || (!MediaUtil.isImageType(contentType) && !MediaUtil.isVideoType(contentType))) { return new Pair<>(0, 0); } @@ -161,18 +164,24 @@ public class MediaUtil { if (MediaUtil.isGif(contentType)) { try { GifDrawable drawable = GlideApp.with(context) - .asGif() - .skipMemoryCache(true) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .load(new DecryptableUri(uri)) - .submit() - .get(); + .asGif() + .skipMemoryCache(true) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .load(new DecryptableUri(uri)) + .submit() + .get(); dimens = new Pair<>(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); } catch (InterruptedException e) { Log.w(TAG, "Was unable to complete work for GIF dimensions.", e); } catch (ExecutionException e) { Log.w(TAG, "Glide experienced an exception while trying to get GIF dimensions.", e); } + } else if (MediaUtil.hasVideoThumbnail(context, uri)) { + Bitmap thumbnail = MediaUtil.getVideoThumbnail(context, uri, 1000); + + if (thumbnail != null) { + dimens = new Pair<>(thumbnail.getWidth(), thumbnail.getHeight()); + } } else { InputStream attachmentStream = null; try { @@ -297,12 +306,16 @@ public class MediaUtil { return (null != contentType) && contentType.equals(VIEW_ONCE); } - public static boolean hasVideoThumbnail(Uri uri) { + public static boolean hasVideoThumbnail(@NonNull Context context, @Nullable Uri uri) { + if (uri == null) { + return false; + } + if (BlobProvider.isAuthority(uri) && MediaUtil.isVideo(BlobProvider.getMimeType(uri)) && Build.VERSION.SDK_INT >= 23) { return true; } - if (uri == null || !isSupportedVideoUriScheme(uri.getScheme())) { + if (!isSupportedVideoUriScheme(uri.getScheme())) { return false; } @@ -313,14 +326,18 @@ public class MediaUtil { } else if (uri.toString().startsWith("file://") && MediaUtil.isVideo(URLConnection.guessContentTypeFromName(uri.toString()))) { return true; + } else if (PartAuthority.isAttachmentUri(uri) && MediaUtil.isVideoType(PartAuthority.getAttachmentContentType(context, uri))) { + return true; } else { return false; } } @WorkerThread - public static @Nullable Bitmap getVideoThumbnail(Context context, Uri uri, long timeUs) { - if ("com.android.providers.media.documents".equals(uri.getAuthority())) { + public static @Nullable Bitmap getVideoThumbnail(@NonNull Context context, @Nullable Uri uri, long timeUs) { + if (uri == null) { + return null; + } else if ("com.android.providers.media.documents".equals(uri.getAuthority())) { long videoId = Long.parseLong(uri.getLastPathSegment().split(":")[1]); return MediaStore.Video.Thumbnails.getThumbnail(context.getContentResolver(), @@ -338,24 +355,44 @@ public class MediaUtil { MediaUtil.isVideo(URLConnection.guessContentTypeFromName(uri.toString()))) { return ThumbnailUtils.createVideoThumbnail(uri.toString().replace("file://", ""), MediaStore.Video.Thumbnails.MINI_KIND); - } else if (BlobProvider.isAuthority(uri) && - MediaUtil.isVideo(BlobProvider.getMimeType(uri)) && - Build.VERSION.SDK_INT >= 23) { + } else if (Build.VERSION.SDK_INT >= 23 && + BlobProvider.isAuthority(uri) && + MediaUtil.isVideo(BlobProvider.getMimeType(uri))) + { try { - MediaDataSource mediaDataSource = BlobProvider.getInstance().getMediaDataSource(context, uri); - MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever(); - - MediaMetadataRetrieverUtil.setDataSource(mediaMetadataRetriever, mediaDataSource); - return mediaMetadataRetriever.getFrameAtTime(timeUs); + MediaDataSource source = BlobProvider.getInstance().getMediaDataSource(context, uri); + return extractFrame(source, timeUs); } catch (IOException e) { - Log.w(TAG, "failed to get thumbnail for video blob uri: " + uri, e); - return null; + Log.w(TAG, "Failed to extract frame for URI: " + uri, e); + } + } else if (Build.VERSION.SDK_INT >= 23 && + PartAuthority.isAttachmentUri(uri) && + MediaUtil.isVideoType(PartAuthority.getAttachmentContentType(context, uri))) + { + try { + AttachmentId attachmentId = PartAuthority.requireAttachmentId(uri); + MediaDataSource source = DatabaseFactory.getAttachmentDatabase(context).mediaDataSourceFor(attachmentId); + return extractFrame(source, timeUs); + } catch (IOException e) { + Log.w(TAG, "Failed to extract frame for URI: " + uri, e); } } return null; } + @RequiresApi(23) + private static @Nullable Bitmap extractFrame(@Nullable MediaDataSource dataSource, long timeUs) throws IOException { + if (dataSource == null) { + return null; + } + + MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever(); + + MediaMetadataRetrieverUtil.setDataSource(mediaMetadataRetriever, dataSource); + return mediaMetadataRetriever.getFrameAtTime(timeUs); + } + public static @Nullable String getDiscreteMimeType(@NonNull String mimeType) { final String[] sections = mimeType.split("/", 2); return sections.length > 1 ? sections[0] : null;