Remove thumbnails from the AttachmentDatabase.
Glide can do everything for us now, including video thumbnails.
This commit is contained in:
parent
5aef1c8a68
commit
9dc33eff3a
48 changed files with 238 additions and 458 deletions
|
@ -133,7 +133,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
|
||||||
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, attachment.getSize());
|
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, attachment.getSize());
|
||||||
intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, attachment.getCaption());
|
intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, attachment.getCaption());
|
||||||
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, leftIsRecent);
|
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, leftIsRecent);
|
||||||
intent.setDataAndType(attachment.getDataUri(), mediaRecord.getContentType());
|
intent.setDataAndType(attachment.getUri(), mediaRecord.getContentType());
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -801,7 +801,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
|
||||||
return new MediaItem(Recipient.live(recipientId).get(),
|
return new MediaItem(Recipient.live(recipientId).get(),
|
||||||
Recipient.live(threadRecipientId).get(),
|
Recipient.live(threadRecipientId).get(),
|
||||||
attachment,
|
attachment,
|
||||||
Objects.requireNonNull(attachment.getDataUri()),
|
Objects.requireNonNull(attachment.getUri()),
|
||||||
mediaRecord.getContentType(),
|
mediaRecord.getContentType(),
|
||||||
mediaRecord.getDate(),
|
mediaRecord.getDate(),
|
||||||
mediaRecord.isOutgoing());
|
mediaRecord.isOutgoing());
|
||||||
|
|
|
@ -106,10 +106,7 @@ public abstract class Attachment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public abstract Uri getDataUri();
|
public abstract Uri getUri();
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public abstract Uri getThumbnailUri();
|
|
||||||
|
|
||||||
public int getTransferState() {
|
public int getTransferState() {
|
||||||
return transferState;
|
return transferState;
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class DatabaseAttachment extends Attachment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Uri getDataUri() {
|
public Uri getUri() {
|
||||||
if (hasData) {
|
if (hasData) {
|
||||||
return PartAuthority.getAttachmentDataUri(attachmentId);
|
return PartAuthority.getAttachmentDataUri(attachmentId);
|
||||||
} else {
|
} 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() {
|
public AttachmentId getAttachmentId() {
|
||||||
return attachmentId;
|
return attachmentId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,7 @@ public class MmsNotificationAttachment extends Attachment {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Uri getDataUri() {
|
public Uri getUri() {
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Uri getThumbnailUri() {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,17 +42,10 @@ public class PointerAttachment extends Attachment {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Uri getDataUri() {
|
public Uri getUri() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Uri getThumbnailUri() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static List<Attachment> forPointers(Optional<List<SignalServiceAttachment>> pointers) {
|
public static List<Attachment> forPointers(Optional<List<SignalServiceAttachment>> pointers) {
|
||||||
List<Attachment> results = new LinkedList<>();
|
List<Attachment> results = new LinkedList<>();
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,7 @@ public class TombstoneAttachment extends Attachment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Uri getDataUri() {
|
public @Nullable Uri getUri() {
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Uri getThumbnailUri() {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import org.thoughtcrime.securesms.stickers.StickerLocator;
|
||||||
public class UriAttachment extends Attachment {
|
public class UriAttachment extends Attachment {
|
||||||
|
|
||||||
private final @NonNull Uri dataUri;
|
private final @NonNull Uri dataUri;
|
||||||
private final @Nullable Uri thumbnailUri;
|
|
||||||
|
|
||||||
public UriAttachment(@NonNull Uri uri,
|
public UriAttachment(@NonNull Uri uri,
|
||||||
@NonNull String contentType,
|
@NonNull String contentType,
|
||||||
|
@ -29,11 +28,10 @@ public class UriAttachment extends Attachment {
|
||||||
@Nullable AudioHash audioHash,
|
@Nullable AudioHash audioHash,
|
||||||
@Nullable TransformProperties transformProperties)
|
@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,
|
public UriAttachment(@NonNull Uri dataUri,
|
||||||
@Nullable Uri thumbnailUri,
|
|
||||||
@NonNull String contentType,
|
@NonNull String contentType,
|
||||||
int transferState,
|
int transferState,
|
||||||
long size,
|
long size,
|
||||||
|
@ -51,22 +49,15 @@ public class UriAttachment extends Attachment {
|
||||||
@Nullable TransformProperties transformProperties)
|
@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);
|
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.dataUri = dataUri;
|
||||||
this.thumbnailUri = thumbnailUri;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public Uri getDataUri() {
|
public Uri getUri() {
|
||||||
return dataUri;
|
return dataUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public Uri getThumbnailUri() {
|
|
||||||
return thumbnailUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
return other != null && other instanceof UriAttachment && ((UriAttachment) other).dataUri.equals(this.dataUri);
|
return other != null && other instanceof UriAttachment && ((UriAttachment) other).dataUri.equals(this.dataUri);
|
||||||
|
|
|
@ -138,13 +138,11 @@ public class FullBackupImporter extends FullBackupBase {
|
||||||
inputStream.readAttachmentTo(output.second, attachment.getLength());
|
inputStream.readAttachmentTo(output.second, attachment.getLength());
|
||||||
|
|
||||||
contentValues.put(AttachmentDatabase.DATA, dataFile.getAbsolutePath());
|
contentValues.put(AttachmentDatabase.DATA, dataFile.getAbsolutePath());
|
||||||
contentValues.put(AttachmentDatabase.THUMBNAIL, (String)null);
|
|
||||||
contentValues.put(AttachmentDatabase.DATA_RANDOM, output.first);
|
contentValues.put(AttachmentDatabase.DATA_RANDOM, output.first);
|
||||||
} catch (BadMacException e) {
|
} catch (BadMacException e) {
|
||||||
Log.w(TAG, "Bad MAC for attachment " + attachment.getAttachmentId() + "! Can't restore it.", e);
|
Log.w(TAG, "Bad MAC for attachment " + attachment.getAttachmentId() + "! Can't restore it.", e);
|
||||||
dataFile.delete();
|
dataFile.delete();
|
||||||
contentValues.put(AttachmentDatabase.DATA, (String) null);
|
contentValues.put(AttachmentDatabase.DATA, (String) null);
|
||||||
contentValues.put(AttachmentDatabase.THUMBNAIL, (String) null);
|
|
||||||
contentValues.put(AttachmentDatabase.DATA_RANDOM, (String) null);
|
contentValues.put(AttachmentDatabase.DATA_RANDOM, (String) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class BorderlessImageView extends FrameLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSlide(@NonNull GlideRequests glideRequests, @NonNull Slide slide) {
|
public void setSlide(@NonNull GlideRequests glideRequests, @NonNull Slide slide) {
|
||||||
boolean showControls = slide.asAttachment().getDataUri() == null;
|
boolean showControls = slide.asAttachment().getUri() == null;
|
||||||
|
|
||||||
if (slide.hasSticker()) {
|
if (slide.hasSticker()) {
|
||||||
image.setFit(new CenterInside());
|
image.setFit(new CenterInside());
|
||||||
|
|
|
@ -242,14 +242,14 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
|
||||||
if (!viewOnceSlides.isEmpty()) {
|
if (!viewOnceSlides.isEmpty()) {
|
||||||
thumbnailView.setVisibility(GONE);
|
thumbnailView.setVisibility(GONE);
|
||||||
attachmentContainerView.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);
|
thumbnailView.setVisibility(VISIBLE);
|
||||||
attachmentContainerView.setVisibility(GONE);
|
attachmentContainerView.setVisibility(GONE);
|
||||||
dismissView.setBackgroundResource(R.drawable.dismiss_background);
|
dismissView.setBackgroundResource(R.drawable.dismiss_background);
|
||||||
if (imageVideoSlides.get(0).hasVideo()) {
|
if (imageVideoSlides.get(0).hasVideo()) {
|
||||||
attachmentVideoOverlayView.setVisibility(VISIBLE);
|
attachmentVideoOverlayView.setVisibility(VISIBLE);
|
||||||
}
|
}
|
||||||
glideRequests.load(new DecryptableUri(imageVideoSlides.get(0).getThumbnailUri()))
|
glideRequests.load(new DecryptableUri(imageVideoSlides.get(0).getUri()))
|
||||||
.centerCrop()
|
.centerCrop()
|
||||||
.override(getContext().getResources().getDimensionPixelSize(R.dimen.quote_thumb_size))
|
.override(getContext().getResources().getDimensionPixelSize(R.dimen.quote_thumb_size))
|
||||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||||
|
|
|
@ -119,7 +119,7 @@ public class SharedContactView extends LinearLayout implements RecipientForeverO
|
||||||
this.activeRecipients.clear();
|
this.activeRecipients.clear();
|
||||||
|
|
||||||
presentContact(contact);
|
presentContact(contact);
|
||||||
presentAvatar(contact.getAvatarAttachment() != null ? contact.getAvatarAttachment().getDataUri() : null);
|
presentAvatar(contact.getAvatarAttachment() != null ? contact.getAvatarAttachment().getUri() : null);
|
||||||
presentActionButtons(ContactUtil.getRecipients(getContext(), contact));
|
presentActionButtons(ContactUtil.getRecipients(getContext(), contact));
|
||||||
|
|
||||||
for (LiveRecipient recipient : activeRecipients.values()) {
|
for (LiveRecipient recipient : activeRecipients.values()) {
|
||||||
|
|
|
@ -279,7 +279,7 @@ public class ThumbnailView extends FrameLayout {
|
||||||
getTransferControls().setVisibility(View.GONE);
|
getTransferControls().setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slide.getThumbnailUri() != null && slide.hasPlayOverlay() &&
|
if (slide.getUri() != null && slide.hasPlayOverlay() &&
|
||||||
(slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE || isPreview))
|
(slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE || isPreview))
|
||||||
{
|
{
|
||||||
this.playOverlay.setVisibility(View.VISIBLE);
|
this.playOverlay.setVisibility(View.VISIBLE);
|
||||||
|
@ -288,12 +288,12 @@ public class ThumbnailView extends FrameLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Util.equals(slide, this.slide)) {
|
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);
|
return new SettableFuture<>(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.slide != null && this.slide.getFastPreflightId() != null &&
|
if (this.slide != null && this.slide.getFastPreflightId() != null &&
|
||||||
(!slide.hasVideo() || Util.equals(this.slide.getThumbnailUri(), slide.getThumbnailUri())) &&
|
(!slide.hasVideo() || Util.equals(this.slide.getUri(), slide.getUri())) &&
|
||||||
Util.equals(this.slide.getFastPreflightId(), slide.getFastPreflightId()))
|
Util.equals(this.slide.getFastPreflightId(), slide.getFastPreflightId()))
|
||||||
{
|
{
|
||||||
Log.i(TAG, "Not re-loading slide for fast preflight: " + 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);
|
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: " +
|
+ ", progress " + slide.getTransferState() + ", fast preflight id: " +
|
||||||
slide.asAttachment().getFastPreflightId());
|
slide.asAttachment().getFastPreflightId());
|
||||||
|
|
||||||
|
@ -327,7 +327,7 @@ public class ThumbnailView extends FrameLayout {
|
||||||
blurhash.setImageDrawable(null);
|
blurhash.setImageDrawable(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slide.getThumbnailUri() != null) {
|
if (slide.getUri() != null) {
|
||||||
if (!MediaUtil.isJpegType(slide.getContentType()) && !MediaUtil.isVideoType(slide.getContentType())) {
|
if (!MediaUtil.isJpegType(slide.getContentType()) && !MediaUtil.isVideoType(slide.getContentType())) {
|
||||||
SettableFuture<Boolean> thumbnailFuture = new SettableFuture<>();
|
SettableFuture<Boolean> thumbnailFuture = new SettableFuture<>();
|
||||||
thumbnailFuture.deferTo(result);
|
thumbnailFuture.deferTo(result);
|
||||||
|
@ -412,7 +412,7 @@ public class ThumbnailView extends FrameLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
private GlideRequest buildThumbnailGlideRequest(@NonNull GlideRequests glideRequests, @NonNull Slide slide) {
|
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)
|
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||||
.transition(withCrossFade()), fit);
|
.transition(withCrossFade()), fit);
|
||||||
|
|
||||||
|
@ -469,10 +469,10 @@ public class ThumbnailView extends FrameLayout {
|
||||||
private class ThumbnailClickDispatcher implements View.OnClickListener {
|
private class ThumbnailClickDispatcher implements View.OnClickListener {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
if (thumbnailClickListener != null &&
|
if (thumbnailClickListener != null &&
|
||||||
slide != null &&
|
slide != null &&
|
||||||
slide.asAttachment().getDataUri() != null &&
|
slide.asAttachment().getUri() != null &&
|
||||||
slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE)
|
slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE)
|
||||||
{
|
{
|
||||||
thumbnailClickListener.onClick(view, slide);
|
thumbnailClickListener.onClick(view, slide);
|
||||||
} else if (parentClickListener != null) {
|
} else if (parentClickListener != null) {
|
||||||
|
|
|
@ -14,7 +14,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||||
import org.thoughtcrime.securesms.attachments.AttachmentId;
|
import org.thoughtcrime.securesms.attachments.AttachmentId;
|
||||||
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
||||||
import org.thoughtcrime.securesms.blurhash.BlurHash;
|
|
||||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||||
|
@ -648,7 +647,7 @@ public class Contact implements Parcelable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
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));
|
dest.writeByte((byte) (isProfile ? 1 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -215,7 +215,7 @@ class ContactFieldAdapter extends RecyclerView.Adapter<ContactFieldAdapter.Conta
|
||||||
Field(@NonNull Avatar avatar) {
|
Field(@NonNull Avatar avatar) {
|
||||||
this.value = "";
|
this.value = "";
|
||||||
this.iconResId = R.drawable.baseline_account_circle_white_24;
|
this.iconResId = R.drawable.baseline_account_circle_white_24;
|
||||||
this.iconUri = avatar.getAttachment() != null ? avatar.getAttachment().getDataUri() : null;
|
this.iconUri = avatar.getAttachment() != null ? avatar.getAttachment().getUri() : null;
|
||||||
this.maxLines = 1;
|
this.maxLines = 1;
|
||||||
this.selectable = avatar;
|
this.selectable = avatar;
|
||||||
this.label = "";
|
this.label = "";
|
||||||
|
|
|
@ -186,11 +186,11 @@ public final class ContactUtil {
|
||||||
intent.putExtra(ContactsContract.Intents.Insert.POSTAL_TYPE, getSystemType(contact.getPostalAddresses().get(0).getType()));
|
intent.putExtra(ContactsContract.Intents.Insert.POSTAL_TYPE, getSystemType(contact.getPostalAddresses().get(0).getType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contact.getAvatarAttachment() != null && contact.getAvatarAttachment().getDataUri() != null) {
|
if (contact.getAvatarAttachment() != null && contact.getAvatarAttachment().getUri() != null) {
|
||||||
try {
|
try {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
|
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
|
||||||
values.put(ContactsContract.CommonDataKinds.Photo.PHOTO, Util.readFully(PartAuthority.getAttachmentStream(context, contact.getAvatarAttachment().getDataUri())));
|
values.put(ContactsContract.CommonDataKinds.Photo.PHOTO, Util.readFully(PartAuthority.getAttachmentStream(context, contact.getAvatarAttachment().getUri())));
|
||||||
|
|
||||||
ArrayList<ContentValues> valuesArray = new ArrayList<>(1);
|
ArrayList<ContentValues> valuesArray = new ArrayList<>(1);
|
||||||
valuesArray.add(values);
|
valuesArray.add(values);
|
||||||
|
|
|
@ -96,7 +96,7 @@ public class SharedContactDetailsActivity extends PassphraseRequiredActivity {
|
||||||
|
|
||||||
presentContact(contact);
|
presentContact(contact);
|
||||||
presentActionButtons(ContactUtil.getRecipients(this, 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()) {
|
for (LiveRecipient recipient : activeRecipients.values()) {
|
||||||
recipient.observe(this, r -> presentActionButtons(Collections.singletonList(r.getId())));
|
recipient.observe(this, r -> presentActionButtons(Collections.singletonList(r.getId())));
|
||||||
|
|
|
@ -676,11 +676,11 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
|
|
||||||
for (Media mediaItem : result.getNonUploadedMedia()) {
|
for (Media mediaItem : result.getNonUploadedMedia()) {
|
||||||
if (MediaUtil.isVideoType(mediaItem.getMimeType())) {
|
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())) {
|
} 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())) {
|
} 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 {
|
} else {
|
||||||
Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.getMimeType() + "'. Skipping.");
|
Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.getMimeType() + "'. Skipping.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -810,7 +810,7 @@ public class ConversationFragment extends LoggingFragment {
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
for (Attachment attachment : attachments) {
|
for (Attachment attachment : attachments) {
|
||||||
Uri uri = attachment.getDataUri() != null ? attachment.getDataUri() : attachment.getThumbnailUri();
|
Uri uri = attachment.getUri();
|
||||||
|
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
mediaList.add(new Media(uri,
|
mediaList.add(new Media(uri,
|
||||||
|
|
|
@ -19,11 +19,8 @@ package org.thoughtcrime.securesms.database;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.media.MediaDataSource;
|
import android.media.MediaDataSource;
|
||||||
import android.media.MediaMetadataRetriever;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
|
@ -58,14 +55,10 @@ import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||||
import org.thoughtcrime.securesms.stickers.StickerLocator;
|
import org.thoughtcrime.securesms.stickers.StickerLocator;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
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.CursorUtil;
|
||||||
import org.thoughtcrime.securesms.util.FileUtils;
|
import org.thoughtcrime.securesms.util.FileUtils;
|
||||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||||
import org.thoughtcrime.securesms.util.MediaMetadataRetrieverUtil;
|
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil.ThumbnailData;
|
|
||||||
import org.thoughtcrime.securesms.util.SetUtil;
|
import org.thoughtcrime.securesms.util.SetUtil;
|
||||||
import org.thoughtcrime.securesms.util.StorageUtil;
|
import org.thoughtcrime.securesms.util.StorageUtil;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
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 org.whispersystems.signalservice.internal.util.JsonUtil;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -90,9 +82,6 @@ import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
|
|
||||||
public class AttachmentDatabase extends Database {
|
public class AttachmentDatabase extends Database {
|
||||||
|
|
||||||
|
@ -111,8 +100,6 @@ public class AttachmentDatabase extends Database {
|
||||||
private static final String TRANSFER_FILE = "transfer_file";
|
private static final String TRANSFER_FILE = "transfer_file";
|
||||||
public static final String SIZE = "data_size";
|
public static final String SIZE = "data_size";
|
||||||
static final String FILE_NAME = "file_name";
|
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";
|
public static final String UNIQUE_ID = "unique_id";
|
||||||
static final String DIGEST = "digest";
|
static final String DIGEST = "digest";
|
||||||
static final String VOICE_NOTE = "voice_note";
|
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 STICKER_EMOJI = "sticker_emoji";
|
||||||
static final String FAST_PREFLIGHT_ID = "fast_preflight_id";
|
static final String FAST_PREFLIGHT_ID = "fast_preflight_id";
|
||||||
public static final String DATA_RANDOM = "data_random";
|
public static final String DATA_RANDOM = "data_random";
|
||||||
private static final String THUMBNAIL_RANDOM = "thumbnail_random";
|
|
||||||
static final String WIDTH = "width";
|
static final String WIDTH = "width";
|
||||||
static final String HEIGHT = "height";
|
static final String HEIGHT = "height";
|
||||||
static final String CAPTION = "caption";
|
static final String CAPTION = "caption";
|
||||||
|
@ -149,11 +135,10 @@ public class AttachmentDatabase extends Database {
|
||||||
|
|
||||||
private static final String[] PROJECTION = new String[] {ROW_ID,
|
private static final String[] PROJECTION = new String[] {ROW_ID,
|
||||||
MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION,
|
MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION,
|
||||||
CDN_NUMBER, CONTENT_LOCATION, DATA, THUMBNAIL,
|
CDN_NUMBER, CONTENT_LOCATION, DATA,
|
||||||
TRANSFER_STATE, SIZE, FILE_NAME, THUMBNAIL,
|
TRANSFER_STATE, SIZE, FILE_NAME, UNIQUE_ID, DIGEST,
|
||||||
THUMBNAIL_ASPECT_RATIO, UNIQUE_ID, DIGEST,
|
|
||||||
FAST_PREFLIGHT_ID, VOICE_NOTE, BORDERLESS, QUOTE, DATA_RANDOM,
|
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,
|
STICKER_PACK_KEY, STICKER_ID, STICKER_EMOJI, DATA_HASH, VISUAL_HASH,
|
||||||
TRANSFORM_PROPERTIES, TRANSFER_FILE, DISPLAY_ORDER,
|
TRANSFORM_PROPERTIES, TRANSFER_FILE, DISPLAY_ORDER,
|
||||||
UPLOAD_TIMESTAMP };
|
UPLOAD_TIMESTAMP };
|
||||||
|
@ -175,15 +160,12 @@ public class AttachmentDatabase extends Database {
|
||||||
DATA + " TEXT, " +
|
DATA + " TEXT, " +
|
||||||
SIZE + " INTEGER, " +
|
SIZE + " INTEGER, " +
|
||||||
FILE_NAME + " TEXT, " +
|
FILE_NAME + " TEXT, " +
|
||||||
THUMBNAIL + " TEXT, " +
|
|
||||||
THUMBNAIL_ASPECT_RATIO + " REAL, " +
|
|
||||||
UNIQUE_ID + " INTEGER NOT NULL, " +
|
UNIQUE_ID + " INTEGER NOT NULL, " +
|
||||||
DIGEST + " BLOB, " +
|
DIGEST + " BLOB, " +
|
||||||
FAST_PREFLIGHT_ID + " TEXT, " +
|
FAST_PREFLIGHT_ID + " TEXT, " +
|
||||||
VOICE_NOTE + " INTEGER DEFAULT 0, " +
|
VOICE_NOTE + " INTEGER DEFAULT 0, " +
|
||||||
BORDERLESS + " INTEGER DEFAULT 0, " +
|
BORDERLESS + " INTEGER DEFAULT 0, " +
|
||||||
DATA_RANDOM + " BLOB, " +
|
DATA_RANDOM + " BLOB, " +
|
||||||
THUMBNAIL_RANDOM + " BLOB, " +
|
|
||||||
QUOTE + " INTEGER DEFAULT 0, " +
|
QUOTE + " INTEGER DEFAULT 0, " +
|
||||||
WIDTH + " INTEGER DEFAULT 0, " +
|
WIDTH + " INTEGER DEFAULT 0, " +
|
||||||
HEIGHT + " 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 + ");"
|
"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;
|
private final AttachmentSecret attachmentSecret;
|
||||||
|
|
||||||
public AttachmentDatabase(Context context, SQLCipherOpenHelper databaseHelper, AttachmentSecret attachmentSecret) {
|
public AttachmentDatabase(Context context, SQLCipherOpenHelper databaseHelper, AttachmentSecret attachmentSecret) {
|
||||||
|
@ -228,29 +206,6 @@ public class AttachmentDatabase extends Database {
|
||||||
else return dataStream;
|
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) {
|
public boolean containsStickerPackId(@NonNull String stickerPackId) {
|
||||||
String selection = STICKER_PACK_ID + " = ?";
|
String selection = STICKER_PACK_ID + " = ?";
|
||||||
String[] args = new String[] { stickerPackId };
|
String[] args = new String[] { stickerPackId };
|
||||||
|
@ -365,12 +320,11 @@ public class AttachmentDatabase extends Database {
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|
||||||
try {
|
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);
|
new String[] {mmsId+""}, null, null, null);
|
||||||
|
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
deleteAttachmentOnDisk(cursor.getString(cursor.getColumnIndex(DATA)),
|
deleteAttachmentOnDisk(cursor.getString(cursor.getColumnIndex(DATA)),
|
||||||
cursor.getString(cursor.getColumnIndex(THUMBNAIL)),
|
|
||||||
cursor.getString(cursor.getColumnIndex(CONTENT_TYPE)),
|
cursor.getString(cursor.getColumnIndex(CONTENT_TYPE)),
|
||||||
new AttachmentId(cursor.getLong(cursor.getColumnIndex(ROW_ID)),
|
new AttachmentId(cursor.getLong(cursor.getColumnIndex(ROW_ID)),
|
||||||
cursor.getLong(cursor.getColumnIndex(UNIQUE_ID))));
|
cursor.getLong(cursor.getColumnIndex(UNIQUE_ID))));
|
||||||
|
@ -418,12 +372,11 @@ public class AttachmentDatabase extends Database {
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|
||||||
try {
|
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);
|
new String[] {mmsId+""}, null, null, null);
|
||||||
|
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
deleteAttachmentOnDisk(cursor.getString(cursor.getColumnIndex(DATA)),
|
deleteAttachmentOnDisk(cursor.getString(cursor.getColumnIndex(DATA)),
|
||||||
cursor.getString(cursor.getColumnIndex(THUMBNAIL)),
|
|
||||||
cursor.getString(cursor.getColumnIndex(CONTENT_TYPE)),
|
cursor.getString(cursor.getColumnIndex(CONTENT_TYPE)),
|
||||||
new AttachmentId(cursor.getLong(cursor.getColumnIndex(ROW_ID)),
|
new AttachmentId(cursor.getLong(cursor.getColumnIndex(ROW_ID)),
|
||||||
cursor.getLong(cursor.getColumnIndex(UNIQUE_ID))));
|
cursor.getLong(cursor.getColumnIndex(UNIQUE_ID))));
|
||||||
|
@ -437,8 +390,6 @@ public class AttachmentDatabase extends Database {
|
||||||
values.put(DATA, (String) null);
|
values.put(DATA, (String) null);
|
||||||
values.put(DATA_RANDOM, (byte[]) null);
|
values.put(DATA_RANDOM, (byte[]) null);
|
||||||
values.put(DATA_HASH, (String) 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(FILE_NAME, (String) null);
|
||||||
values.put(CAPTION, (String) null);
|
values.put(CAPTION, (String) null);
|
||||||
values.put(SIZE, 0);
|
values.put(SIZE, 0);
|
||||||
|
@ -463,7 +414,7 @@ public class AttachmentDatabase extends Database {
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
|
|
||||||
try (Cursor cursor = database.query(TABLE_NAME,
|
try (Cursor cursor = database.query(TABLE_NAME,
|
||||||
new String[]{DATA, THUMBNAIL, CONTENT_TYPE},
|
new String[]{DATA, CONTENT_TYPE},
|
||||||
PART_ID_WHERE,
|
PART_ID_WHERE,
|
||||||
id.toStrings(),
|
id.toStrings(),
|
||||||
null,
|
null,
|
||||||
|
@ -475,11 +426,10 @@ public class AttachmentDatabase extends Database {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String data = cursor.getString(cursor.getColumnIndex(DATA));
|
String data = cursor.getString(cursor.getColumnIndex(DATA));
|
||||||
String thumbnail = cursor.getString(cursor.getColumnIndex(THUMBNAIL));
|
|
||||||
String contentType = cursor.getString(cursor.getColumnIndex(CONTENT_TYPE));
|
String contentType = cursor.getString(cursor.getColumnIndex(CONTENT_TYPE));
|
||||||
|
|
||||||
database.delete(TABLE_NAME, PART_ID_WHERE, id.toStrings());
|
database.delete(TABLE_NAME, PART_ID_WHERE, id.toStrings());
|
||||||
deleteAttachmentOnDisk(data, thumbnail, contentType, id);
|
deleteAttachmentOnDisk(data, contentType, id);
|
||||||
notifyAttachmentListeners();
|
notifyAttachmentListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -502,10 +452,9 @@ public class AttachmentDatabase extends Database {
|
||||||
filesOnDisk.add(file.getAbsolutePath());
|
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()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
filesInDb.add(CursorUtil.requireString(cursor, DATA));
|
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,
|
private void deleteAttachmentOnDisk(@Nullable String data,
|
||||||
@Nullable String thumbnail,
|
|
||||||
@Nullable String contentType,
|
@Nullable String contentType,
|
||||||
@NonNull AttachmentId attachmentId)
|
@NonNull AttachmentId attachmentId)
|
||||||
{
|
{
|
||||||
|
@ -561,8 +509,6 @@ public class AttachmentDatabase extends Database {
|
||||||
values.putNull(DATA);
|
values.putNull(DATA);
|
||||||
values.putNull(DATA_RANDOM);
|
values.putNull(DATA_RANDOM);
|
||||||
values.putNull(DATA_HASH);
|
values.putNull(DATA_HASH);
|
||||||
values.putNull(THUMBNAIL);
|
|
||||||
values.putNull(THUMBNAIL_RANDOM);
|
|
||||||
deletedCount += database.update(TABLE_NAME, values, PART_ID_WHERE, weakReference.toStrings());
|
deletedCount += database.update(TABLE_NAME, values, PART_ID_WHERE, weakReference.toStrings());
|
||||||
}
|
}
|
||||||
database.setTransactionSuccessful();
|
database.setTransactionSuccessful();
|
||||||
|
@ -581,15 +527,7 @@ public class AttachmentDatabase extends Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(thumbnail)) {
|
if (MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType)) {
|
||||||
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) {
|
|
||||||
Glide.get(context).clearDiskCache();
|
Glide.get(context).clearDiskCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -623,22 +561,17 @@ public class AttachmentDatabase extends Database {
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
DataInfo oldInfo = getAttachmentDataFileInfo(attachmentId, DATA);
|
DataInfo oldInfo = getAttachmentDataFileInfo(attachmentId, DATA);
|
||||||
DataInfo dataInfo = setAttachmentData(inputStream, false, attachmentId);
|
DataInfo dataInfo = setAttachmentData(inputStream, attachmentId);
|
||||||
File transferFile = getTransferFile(databaseHelper.getReadableDatabase(), attachmentId);
|
File transferFile = getTransferFile(databaseHelper.getReadableDatabase(), attachmentId);
|
||||||
|
|
||||||
if (oldInfo != null) {
|
if (oldInfo != null) {
|
||||||
updateAttachmentDataHash(database, oldInfo.hash, dataInfo);
|
updateAttachmentDataHash(database, oldInfo.hash, dataInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (placeholder != null && placeholder.isQuote() && !placeholder.getContentType().startsWith("image")) {
|
values.put(DATA, dataInfo.file.getAbsolutePath());
|
||||||
values.put(THUMBNAIL, dataInfo.file.getAbsolutePath());
|
values.put(SIZE, dataInfo.length);
|
||||||
values.put(THUMBNAIL_RANDOM, dataInfo.random);
|
values.put(DATA_RANDOM, dataInfo.random);
|
||||||
} else {
|
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);
|
String visualHashString = getVisualHashStringOrNull(placeholder);
|
||||||
if (visualHashString != null) {
|
if (visualHashString != null) {
|
||||||
|
@ -662,8 +595,6 @@ public class AttachmentDatabase extends Database {
|
||||||
//noinspection ResultOfMethodCallIgnored
|
//noinspection ResultOfMethodCallIgnored
|
||||||
transferFile.delete();
|
transferFile.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
thumbnailExecutor.submit(new ThumbnailFetchCallable(attachmentId, STANDARD_THUMB_TIME));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @Nullable String getVisualHashStringOrNull(@Nullable Attachment attachment) {
|
private static @Nullable String getVisualHashStringOrNull(@Nullable Attachment attachment) {
|
||||||
|
@ -846,7 +777,6 @@ public class AttachmentDatabase extends Database {
|
||||||
|
|
||||||
DataInfo dataInfo = setAttachmentData(destination,
|
DataInfo dataInfo = setAttachmentData(destination,
|
||||||
mediaStream.getStream(),
|
mediaStream.getStream(),
|
||||||
false,
|
|
||||||
databaseAttachment.getAttachmentId());
|
databaseAttachment.getAttachmentId());
|
||||||
|
|
||||||
ContentValues contentValues = new ContentValues();
|
ContentValues contentValues = new ContentValues();
|
||||||
|
@ -1055,16 +985,8 @@ public class AttachmentDatabase extends Database {
|
||||||
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||||
Cursor cursor = null;
|
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 {
|
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);
|
null, null, null);
|
||||||
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
@ -1074,7 +996,7 @@ public class AttachmentDatabase extends Database {
|
||||||
|
|
||||||
return new DataInfo(new File(cursor.getString(cursor.getColumnIndexOrThrow(dataType))),
|
return new DataInfo(new File(cursor.getString(cursor.getColumnIndexOrThrow(dataType))),
|
||||||
cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)),
|
cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)),
|
||||||
cursor.getBlob(cursor.getColumnIndexOrThrow(randomColumn)),
|
cursor.getBlob(cursor.getColumnIndexOrThrow(DATA_RANDOM)),
|
||||||
cursor.getString(cursor.getColumnIndexOrThrow(DATA_HASH)));
|
cursor.getString(cursor.getColumnIndexOrThrow(DATA_HASH)));
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
@ -1087,26 +1009,24 @@ public class AttachmentDatabase extends Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NonNull DataInfo setAttachmentData(@NonNull Uri uri,
|
private @NonNull DataInfo setAttachmentData(@NonNull Uri uri,
|
||||||
boolean isThumbnail,
|
|
||||||
@Nullable AttachmentId attachmentId)
|
@Nullable AttachmentId attachmentId)
|
||||||
throws MmsException
|
throws MmsException
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
InputStream inputStream = PartAuthority.getAttachmentStream(context, uri);
|
InputStream inputStream = PartAuthority.getAttachmentStream(context, uri);
|
||||||
return setAttachmentData(inputStream, isThumbnail, attachmentId);
|
return setAttachmentData(inputStream, attachmentId);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new MmsException(e);
|
throw new MmsException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NonNull DataInfo setAttachmentData(@NonNull InputStream in,
|
private @NonNull DataInfo setAttachmentData(@NonNull InputStream in,
|
||||||
boolean isThumbnail,
|
|
||||||
@Nullable AttachmentId attachmentId)
|
@Nullable AttachmentId attachmentId)
|
||||||
throws MmsException
|
throws MmsException
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
File dataFile = newFile();
|
File dataFile = newFile();
|
||||||
return setAttachmentData(dataFile, in, isThumbnail, attachmentId);
|
return setAttachmentData(dataFile, in, attachmentId);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new MmsException(e);
|
throw new MmsException(e);
|
||||||
}
|
}
|
||||||
|
@ -1119,7 +1039,6 @@ public class AttachmentDatabase extends Database {
|
||||||
|
|
||||||
private @NonNull DataInfo setAttachmentData(@NonNull File destination,
|
private @NonNull DataInfo setAttachmentData(@NonNull File destination,
|
||||||
@NonNull InputStream in,
|
@NonNull InputStream in,
|
||||||
boolean isThumbnail,
|
|
||||||
@Nullable AttachmentId attachmentId)
|
@Nullable AttachmentId attachmentId)
|
||||||
throws MmsException
|
throws MmsException
|
||||||
{
|
{
|
||||||
|
@ -1130,18 +1049,16 @@ public class AttachmentDatabase extends Database {
|
||||||
long length = Util.copy(digestInputStream, out.second);
|
long length = Util.copy(digestInputStream, out.second);
|
||||||
String hash = Base64.encodeBytes(digestInputStream.getMessageDigest().digest());
|
String hash = Base64.encodeBytes(digestInputStream.getMessageDigest().digest());
|
||||||
|
|
||||||
if (!isThumbnail) {
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
Optional<DataInfo> sharedDataInfo = findDuplicateDataFileInfo(database, hash, attachmentId);
|
||||||
Optional<DataInfo> sharedDataInfo = findDuplicateDataFileInfo(database, hash, attachmentId);
|
if (sharedDataInfo.isPresent()) {
|
||||||
if (sharedDataInfo.isPresent()) {
|
Log.i(TAG, "[setAttachmentData] Duplicate data file found! " + sharedDataInfo.get().file.getAbsolutePath());
|
||||||
Log.i(TAG, "[setAttachmentData] Duplicate data file found! " + sharedDataInfo.get().file.getAbsolutePath());
|
if (!destination.equals(sharedDataInfo.get().file) && destination.delete()) {
|
||||||
if (!destination.equals(sharedDataInfo.get().file) && destination.delete()) {
|
Log.i(TAG, "[setAttachmentData] Deleted original file. " + destination);
|
||||||
Log.i(TAG, "[setAttachmentData] Deleted original file. " + destination);
|
|
||||||
}
|
|
||||||
return sharedDataInfo.get();
|
|
||||||
} else {
|
|
||||||
Log.i(TAG, "[setAttachmentData] No matching attachment data found. " + destination.getAbsolutePath());
|
|
||||||
}
|
}
|
||||||
|
return sharedDataInfo.get();
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "[setAttachmentData] No matching attachment data found. " + destination.getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DataInfo(destination, length, out.first, hash);
|
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)),
|
result.add(new DatabaseAttachment(new AttachmentId(object.getLong(ROW_ID), object.getLong(UNIQUE_ID)),
|
||||||
object.getLong(MMS_ID),
|
object.getLong(MMS_ID),
|
||||||
!TextUtils.isEmpty(object.getString(DATA)),
|
!TextUtils.isEmpty(object.getString(DATA)),
|
||||||
!TextUtils.isEmpty(object.getString(THUMBNAIL)),
|
MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType),
|
||||||
contentType,
|
contentType,
|
||||||
object.getInt(TRANSFER_STATE),
|
object.getInt(TRANSFER_STATE),
|
||||||
object.getLong(SIZE),
|
object.getLong(SIZE),
|
||||||
|
@ -1254,7 +1171,7 @@ public class AttachmentDatabase extends Database {
|
||||||
cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID))),
|
cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID))),
|
||||||
cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)),
|
cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)),
|
||||||
!cursor.isNull(cursor.getColumnIndexOrThrow(DATA)),
|
!cursor.isNull(cursor.getColumnIndexOrThrow(DATA)),
|
||||||
!cursor.isNull(cursor.getColumnIndexOrThrow(THUMBNAIL)),
|
MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType),
|
||||||
contentType,
|
contentType,
|
||||||
cursor.getInt(cursor.getColumnIndexOrThrow(TRANSFER_STATE)),
|
cursor.getInt(cursor.getColumnIndexOrThrow(TRANSFER_STATE)),
|
||||||
cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)),
|
cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)),
|
||||||
|
@ -1296,10 +1213,9 @@ public class AttachmentDatabase extends Database {
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
DataInfo dataInfo = null;
|
DataInfo dataInfo = null;
|
||||||
long uniqueId = System.currentTimeMillis();
|
long uniqueId = System.currentTimeMillis();
|
||||||
long thumbnailTimeUs;
|
|
||||||
|
|
||||||
if (attachment.getDataUri() != null) {
|
if (attachment.getUri() != null) {
|
||||||
dataInfo = setAttachmentData(attachment.getDataUri(), false, null);
|
dataInfo = setAttachmentData(attachment.getUri(), null);
|
||||||
Log.d(TAG, "Wrote part to file: " + dataInfo.file.getAbsolutePath());
|
Log.d(TAG, "Wrote part to file: " + dataInfo.file.getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1342,11 +1258,9 @@ public class AttachmentDatabase extends Database {
|
||||||
if (attachment.getTransformProperties().isVideoEdited()) {
|
if (attachment.getTransformProperties().isVideoEdited()) {
|
||||||
contentValues.putNull(VISUAL_HASH);
|
contentValues.putNull(VISUAL_HASH);
|
||||||
contentValues.put(TRANSFORM_PROPERTIES, attachment.getTransformProperties().serialize());
|
contentValues.put(TRANSFORM_PROPERTIES, attachment.getTransformProperties().serialize());
|
||||||
thumbnailTimeUs = Math.max(STANDARD_THUMB_TIME, attachment.getTransformProperties().videoTrimStartTimeUs);
|
|
||||||
} else {
|
} else {
|
||||||
contentValues.put(VISUAL_HASH, getVisualHashStringOrNull(template));
|
contentValues.put(VISUAL_HASH, getVisualHashStringOrNull(template));
|
||||||
contentValues.put(TRANSFORM_PROPERTIES, template.getTransformProperties().serialize());
|
contentValues.put(TRANSFORM_PROPERTIES, template.getTransformProperties().serialize());
|
||||||
thumbnailTimeUs = STANDARD_THUMB_TIME;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attachment.isSticker()) {
|
if (attachment.isSticker()) {
|
||||||
|
@ -1370,38 +1284,6 @@ public class AttachmentDatabase extends Database {
|
||||||
boolean notifyPacks = attachment.isSticker() && !hasStickerAttachments();
|
boolean notifyPacks = attachment.isSticker() && !hasStickerAttachments();
|
||||||
long rowId = database.insert(TABLE_NAME, null, contentValues);
|
long rowId = database.insert(TABLE_NAME, null, contentValues);
|
||||||
AttachmentId attachmentId = new AttachmentId(rowId, uniqueId);
|
AttachmentId attachmentId = new AttachmentId(rowId, uniqueId);
|
||||||
Uri thumbnailUri = attachment.getThumbnailUri();
|
|
||||||
boolean hasThumbnail = false;
|
|
||||||
|
|
||||||
if (thumbnailUri != null) {
|
|
||||||
try (InputStream attachmentStream = PartAuthority.getAttachmentStream(context, thumbnailUri)) {
|
|
||||||
Pair<Integer, Integer> 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) {
|
if (notifyPacks) {
|
||||||
notifyStickerPackListeners();
|
notifyStickerPackListeners();
|
||||||
|
@ -1423,35 +1305,6 @@ public class AttachmentDatabase extends Database {
|
||||||
return null;
|
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
|
@WorkerThread
|
||||||
public void writeAudioHash(@NonNull AttachmentId attachmentId, @Nullable AudioWaveFormData audioWaveForm) {
|
public void writeAudioHash(@NonNull AttachmentId attachmentId, @Nullable AudioWaveFormData audioWaveForm) {
|
||||||
Log.i(TAG, "updating part audio wave form for #" + attachmentId);
|
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());
|
database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings());
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
class ThumbnailFetchCallable implements Callable<InputStream> {
|
|
||||||
|
|
||||||
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)
|
@RequiresApi(23)
|
||||||
public @Nullable MediaDataSource mediaDataSourceFor(@NonNull AttachmentId attachmentId) {
|
public @Nullable MediaDataSource mediaDataSourceFor(@NonNull AttachmentId attachmentId) {
|
||||||
|
|
|
@ -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 + ", "
|
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.CONTENT_TYPE + ", "
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.THUMBNAIL_ASPECT_RATIO + ", "
|
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", "
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", "
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + ", "
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + ", "
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.TRANSFER_STATE + ", "
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.TRANSFER_STATE + ", "
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + ", "
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + ", "
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FILE_NAME + ", "
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FILE_NAME + ", "
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DATA + ", "
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DATA + ", "
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.THUMBNAIL + ", "
|
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CDN_NUMBER + ", "
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CDN_NUMBER + ", "
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", "
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", "
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_DISPOSITION + ", "
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_DISPOSITION + ", "
|
||||||
|
|
|
@ -213,7 +213,6 @@ public class MmsDatabase extends MessageDatabase {
|
||||||
"'" + AttachmentDatabase.SIZE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + ", " +
|
"'" + AttachmentDatabase.SIZE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + ", " +
|
||||||
"'" + AttachmentDatabase.FILE_NAME + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FILE_NAME + ", " +
|
"'" + AttachmentDatabase.FILE_NAME + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FILE_NAME + ", " +
|
||||||
"'" + AttachmentDatabase.DATA + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DATA + ", " +
|
"'" + AttachmentDatabase.DATA + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DATA + ", " +
|
||||||
"'" + AttachmentDatabase.THUMBNAIL + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.THUMBNAIL + ", " +
|
|
||||||
"'" + AttachmentDatabase.CONTENT_TYPE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_TYPE + ", " +
|
"'" + AttachmentDatabase.CONTENT_TYPE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_TYPE + ", " +
|
||||||
"'" + AttachmentDatabase.CDN_NUMBER + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CDN_NUMBER + ", " +
|
"'" + AttachmentDatabase.CDN_NUMBER + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CDN_NUMBER + ", " +
|
||||||
"'" + AttachmentDatabase.CONTENT_LOCATION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", " +
|
"'" + AttachmentDatabase.CONTENT_LOCATION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", " +
|
||||||
|
|
|
@ -411,7 +411,6 @@ public class MmsSmsDatabase extends Database {
|
||||||
"'" + AttachmentDatabase.SIZE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + ", " +
|
"'" + AttachmentDatabase.SIZE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + ", " +
|
||||||
"'" + AttachmentDatabase.FILE_NAME + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FILE_NAME + ", " +
|
"'" + AttachmentDatabase.FILE_NAME + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FILE_NAME + ", " +
|
||||||
"'" + AttachmentDatabase.DATA + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DATA + ", " +
|
"'" + AttachmentDatabase.DATA + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DATA + ", " +
|
||||||
"'" + AttachmentDatabase.THUMBNAIL + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.THUMBNAIL + ", " +
|
|
||||||
"'" + AttachmentDatabase.CONTENT_TYPE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_TYPE + ", " +
|
"'" + AttachmentDatabase.CONTENT_TYPE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_TYPE + ", " +
|
||||||
"'" + AttachmentDatabase.CDN_NUMBER + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CDN_NUMBER + ", " +
|
"'" + AttachmentDatabase.CDN_NUMBER + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CDN_NUMBER + ", " +
|
||||||
"'" + AttachmentDatabase.CONTENT_LOCATION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", " +
|
"'" + AttachmentDatabase.CONTENT_LOCATION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", " +
|
||||||
|
|
|
@ -1095,7 +1095,7 @@ public class ThreadDatabase extends Database {
|
||||||
Slide thumbnail = Optional.fromNullable(slideDeck.getThumbnailSlide()).or(Optional.fromNullable(slideDeck.getStickerSlide())).orNull();
|
Slide thumbnail = Optional.fromNullable(slideDeck.getThumbnailSlide()).or(Optional.fromNullable(slideDeck.getStickerSlide())).orNull();
|
||||||
|
|
||||||
if (thumbnail != null && !((MmsMessageRecord) record).isViewOnce()) {
|
if (thumbnail != null && !((MmsMessageRecord) record).isViewOnce()) {
|
||||||
return thumbnail.getThumbnailUri();
|
return thumbnail.getUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -57,6 +57,7 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
|
import org.thoughtcrime.securesms.util.CursorUtil;
|
||||||
import org.thoughtcrime.securesms.util.FileUtils;
|
import org.thoughtcrime.securesms.util.FileUtils;
|
||||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||||
import org.thoughtcrime.securesms.util.SqlUtil;
|
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 UNKNOWN_STORAGE_FIELDS = 71;
|
||||||
private static final int STICKER_CONTENT_TYPE = 72;
|
private static final int STICKER_CONTENT_TYPE = 72;
|
||||||
private static final int STICKER_EMOJI_IN_NOTIFICATIONS = 73;
|
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 static final String DATABASE_NAME = "signal.db";
|
||||||
|
|
||||||
private final Context context;
|
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");
|
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();
|
db.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
|
|
|
@ -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 {
|
try {
|
||||||
Mac mac = Mac.getInstance("HmacSHA256");
|
Mac mac = Mac.getInstance("HmacSHA256");
|
||||||
mac.init(new SecretKeySpec(masterKey, "HmacSHA256"));
|
mac.init(new SecretKeySpec(masterKey, "HmacSHA256"));
|
||||||
|
|
|
@ -249,7 +249,7 @@ public final class AttachmentCompressionJob extends BaseJob {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
BitmapUtil.ScaleResult scaleResult = BitmapUtil.createScaledBytes(context,
|
BitmapUtil.ScaleResult scaleResult = BitmapUtil.createScaledBytes(context,
|
||||||
new DecryptableStreamUriLoader.DecryptableUri(attachment.getDataUri()),
|
new DecryptableStreamUriLoader.DecryptableUri(attachment.getUri()),
|
||||||
constraints);
|
constraints);
|
||||||
|
|
||||||
return new MediaStream(new ByteArrayInputStream(scaleResult.getBitmap()),
|
return new MediaStream(new ByteArrayInputStream(scaleResult.getBitmap()),
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
package org.thoughtcrime.securesms.jobs;
|
package org.thoughtcrime.securesms.jobs;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.media.MediaDataSource;
|
|
||||||
import android.media.MediaMetadataRetriever;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.WorkerThread;
|
|
||||||
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.thoughtcrime.securesms.R;
|
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.mms.PartAuthority;
|
||||||
import org.thoughtcrime.securesms.service.GenericForegroundService;
|
import org.thoughtcrime.securesms.service.GenericForegroundService;
|
||||||
import org.thoughtcrime.securesms.service.NotificationController;
|
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.thoughtcrime.securesms.util.MediaUtil;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.TimeUnit;
|
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 {
|
private @NonNull SignalServiceAttachment getAttachmentFor(Attachment attachment, @Nullable NotificationController notification, @Nullable ResumableUploadSpec resumableUploadSpec) throws InvalidAttachmentException {
|
||||||
try {
|
try {
|
||||||
if (attachment.getDataUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!");
|
if (attachment.getUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!");
|
||||||
InputStream is = PartAuthority.getAttachmentStream(context, attachment.getDataUri());
|
InputStream is = PartAuthority.getAttachmentStream(context, attachment.getUri());
|
||||||
SignalServiceAttachment.Builder builder = SignalServiceAttachment.newStreamBuilder()
|
SignalServiceAttachment.Builder builder = SignalServiceAttachment.newStreamBuilder()
|
||||||
.withStream(is)
|
.withStream(is)
|
||||||
.withContentType(attachment.getContentType())
|
.withContentType(attachment.getContentType())
|
||||||
|
@ -193,43 +189,34 @@ public final class AttachmentUploadJob extends BaseJob {
|
||||||
|
|
||||||
private @Nullable String getImageBlurHash(@NonNull Attachment attachment) throws IOException {
|
private @Nullable String getImageBlurHash(@NonNull Attachment attachment) throws IOException {
|
||||||
if (attachment.getBlurHash() != null) return attachment.getBlurHash().getHash();
|
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 {
|
private @Nullable String getVideoBlurHash(@NonNull Attachment attachment) throws IOException {
|
||||||
if (attachment.getThumbnailUri() != null) {
|
if (attachment.getBlurHash() != null) {
|
||||||
return BlurHashEncoder.encode(PartAuthority.getAttachmentStream(context, attachment.getThumbnailUri()));
|
return attachment.getBlurHash().getHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attachment.getBlurHash() != null) return attachment.getBlurHash().getHash();
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < 23) {
|
if (Build.VERSION.SDK_INT < 23) {
|
||||||
Log.w(TAG, "Video thumbnails not supported...");
|
Log.w(TAG, "Video thumbnails not supported...");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (MediaDataSource dataSource = DatabaseFactory.getAttachmentDatabase(context).mediaDataSourceFor(attachmentId)) {
|
Bitmap bitmap = MediaUtil.getVideoThumbnail(context, Objects.requireNonNull(attachment.getUri()), 1000);
|
||||||
if (dataSource == null) return null;
|
|
||||||
|
|
||||||
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
if (bitmap != null) {
|
||||||
MediaMetadataRetrieverUtil.setDataSource(retriever, dataSource);
|
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) {
|
return hash;
|
||||||
Bitmap thumb = Bitmap.createScaledBitmap(bitmap, 100, 100, false);
|
} else {
|
||||||
bitmap.recycle();
|
return null;
|
||||||
|
|
||||||
Log.i(TAG, "Generated video thumbnail...");
|
|
||||||
String hash = BlurHashEncoder.encode(thumb);
|
|
||||||
thumb.recycle();
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.database.MessageDatabase;
|
import org.thoughtcrime.securesms.database.MessageDatabase;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
@ -273,7 +272,7 @@ public final class MmsSendJob extends SendJob {
|
||||||
|
|
||||||
for (Attachment attachment : scaledAttachments) {
|
for (Attachment attachment : scaledAttachments) {
|
||||||
try {
|
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();
|
String fileName = attachment.getFileName();
|
||||||
PduPart part = new PduPart();
|
PduPart part = new PduPart();
|
||||||
|
@ -296,7 +295,7 @@ public final class MmsSendJob extends SendJob {
|
||||||
int index = fileName.lastIndexOf(".");
|
int index = fileName.lastIndexOf(".");
|
||||||
String contentId = (index == -1) ? fileName : fileName.substring(0, index);
|
String contentId = (index == -1) ? fileName : fileName.substring(0, index);
|
||||||
part.setContentId(contentId.getBytes());
|
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);
|
body.addPart(part);
|
||||||
size += getPartSize(part);
|
size += getPartSize(part);
|
||||||
|
|
|
@ -1673,7 +1673,6 @@ public final class PushProcessMessageJob extends BaseJob {
|
||||||
|
|
||||||
if (stickerRecord != null) {
|
if (stickerRecord != null) {
|
||||||
return Optional.of(new UriAttachment(stickerRecord.getUri(),
|
return Optional.of(new UriAttachment(stickerRecord.getUri(),
|
||||||
stickerRecord.getUri(),
|
|
||||||
stickerRecord.getContentType(),
|
stickerRecord.getContentType(),
|
||||||
AttachmentDatabase.TRANSFER_PROGRESS_DONE,
|
AttachmentDatabase.TRANSFER_PROGRESS_DONE,
|
||||||
stickerRecord.getSize(),
|
stickerRecord.getSize(),
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.jobs;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
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.Base64;
|
||||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
|
||||||
import org.thoughtcrime.securesms.util.Hex;
|
import org.thoughtcrime.securesms.util.Hex;
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
@ -118,8 +118,8 @@ public abstract class PushSendJob extends SendJob {
|
||||||
|
|
||||||
protected SignalServiceAttachment getAttachmentFor(Attachment attachment) {
|
protected SignalServiceAttachment getAttachmentFor(Attachment attachment) {
|
||||||
try {
|
try {
|
||||||
if (attachment.getDataUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!");
|
if (attachment.getUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!");
|
||||||
InputStream is = PartAuthority.getAttachmentStream(context, attachment.getDataUri());
|
InputStream is = PartAuthority.getAttachmentStream(context, attachment.getUri());
|
||||||
return SignalServiceAttachment.newStreamBuilder()
|
return SignalServiceAttachment.newStreamBuilder()
|
||||||
.withStream(is)
|
.withStream(is)
|
||||||
.withContentType(attachment.getContentType())
|
.withContentType(attachment.getContentType())
|
||||||
|
@ -192,14 +192,26 @@ public abstract class PushSendJob extends SendJob {
|
||||||
final SignalServiceAttachmentRemoteId remoteId = SignalServiceAttachmentRemoteId.from(attachment.getLocation());
|
final SignalServiceAttachmentRemoteId remoteId = SignalServiceAttachmentRemoteId.from(attachment.getLocation());
|
||||||
final byte[] key = Base64.decode(attachment.getKey());
|
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(),
|
return new SignalServiceAttachmentPointer(attachment.getCdnNumber(),
|
||||||
remoteId,
|
remoteId,
|
||||||
attachment.getContentType(),
|
attachment.getContentType(),
|
||||||
key,
|
key,
|
||||||
Optional.of(Util.toIntExact(attachment.getSize())),
|
Optional.of(Util.toIntExact(attachment.getSize())),
|
||||||
Optional.absent(),
|
Optional.absent(),
|
||||||
attachment.getWidth(),
|
width,
|
||||||
attachment.getHeight(),
|
height,
|
||||||
Optional.fromNullable(attachment.getDigest()),
|
Optional.fromNullable(attachment.getDigest()),
|
||||||
Optional.fromNullable(attachment.getFileName()),
|
Optional.fromNullable(attachment.getFileName()),
|
||||||
attachment.isVoiceNote(),
|
attachment.isVoiceNote(),
|
||||||
|
@ -240,13 +252,17 @@ public abstract class PushSendJob extends SendJob {
|
||||||
String thumbnailType = MediaUtil.IMAGE_JPEG;
|
String thumbnailType = MediaUtil.IMAGE_JPEG;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (MediaUtil.isImageType(attachment.getContentType()) && attachment.getDataUri() != null) {
|
if (MediaUtil.isImageType(attachment.getContentType()) && attachment.getUri() != null) {
|
||||||
Bitmap.CompressFormat format = BitmapUtil.getCompressFormatForContentType(attachment.getContentType());
|
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();
|
thumbnailType = attachment.getContentType();
|
||||||
} else if (MediaUtil.isVideoType(attachment.getContentType()) && attachment.getThumbnailUri() != null) {
|
} else if (Build.VERSION.SDK_INT >= 23 && MediaUtil.isVideoType(attachment.getContentType()) && attachment.getUri() != null) {
|
||||||
thumbnailData = BitmapUtil.createScaledBytes(context, new DecryptableStreamUriLoader.DecryptableUri(attachment.getThumbnailUri()), 100, 100, 500 * 1024);
|
Bitmap bitmap = MediaUtil.getVideoThumbnail(context, attachment.getUri(), 1000);
|
||||||
|
|
||||||
|
if (bitmap != null) {
|
||||||
|
thumbnailData = BitmapUtil.createScaledBytes(context, bitmap, 100, 100, 500 * 1024);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thumbnailData != null) {
|
if (thumbnailData != null) {
|
||||||
|
|
|
@ -335,7 +335,6 @@ public class LinkPreviewRepository {
|
||||||
Uri uri = BlobProvider.getInstance().forData(bytes).createForSingleSessionInMemory();
|
Uri uri = BlobProvider.getInstance().forData(bytes).createForSingleSessionInMemory();
|
||||||
|
|
||||||
return Optional.of(new UriAttachment(uri,
|
return Optional.of(new UriAttachment(uri,
|
||||||
uri,
|
|
||||||
contentType,
|
contentType,
|
||||||
AttachmentDatabase.TRANSFER_PROGRESS_STARTED,
|
AttachmentDatabase.TRANSFER_PROGRESS_STARTED,
|
||||||
bytes.length,
|
bytes.length,
|
||||||
|
|
|
@ -47,8 +47,8 @@ final class MediaActions {
|
||||||
List<SaveAttachmentTask.Attachment> attachments = new LinkedList<>();
|
List<SaveAttachmentTask.Attachment> attachments = new LinkedList<>();
|
||||||
|
|
||||||
for (MediaDatabase.MediaRecord mediaRecord : mediaRecords) {
|
for (MediaDatabase.MediaRecord mediaRecord : mediaRecords) {
|
||||||
if (mediaRecord.getAttachment().getDataUri() != null) {
|
if (mediaRecord.getAttachment().getUri() != null) {
|
||||||
attachments.add(new SaveAttachmentTask.Attachment(mediaRecord.getAttachment().getDataUri(),
|
attachments.add(new SaveAttachmentTask.Attachment(mediaRecord.getAttachment().getUri(),
|
||||||
mediaRecord.getContentType(),
|
mediaRecord.getContentType(),
|
||||||
mediaRecord.getDate(),
|
mediaRecord.getDate(),
|
||||||
mediaRecord.getAttachment().getFileName()));
|
mediaRecord.getAttachment().getFileName()));
|
||||||
|
|
|
@ -210,7 +210,7 @@ public final class MediaOverviewPageFragment extends Fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleMediaPreviewClick(@NonNull MediaDatabase.MediaRecord mediaRecord) {
|
private void handleMediaPreviewClick(@NonNull MediaDatabase.MediaRecord mediaRecord) {
|
||||||
if (mediaRecord.getAttachment().getDataUri() == null) {
|
if (mediaRecord.getAttachment().getUri() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ public final class MediaOverviewPageFragment extends Fragment
|
||||||
intent.putExtra(MediaPreviewActivity.SHOW_THREAD_EXTRA, threadId == MediaDatabase.ALL_THREADS);
|
intent.putExtra(MediaPreviewActivity.SHOW_THREAD_EXTRA, threadId == MediaDatabase.ALL_THREADS);
|
||||||
intent.putExtra(MediaPreviewActivity.SORTING_EXTRA, sorting.ordinal());
|
intent.putExtra(MediaPreviewActivity.SORTING_EXTRA, sorting.ordinal());
|
||||||
|
|
||||||
intent.setDataAndType(mediaRecord.getAttachment().getDataUri(), mediaRecord.getContentType());
|
intent.setDataAndType(mediaRecord.getAttachment().getUri(), mediaRecord.getContentType());
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
} else {
|
} else {
|
||||||
if (!MediaUtil.isAudio(attachment)) {
|
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) {
|
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 intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
|
|
@ -29,7 +29,7 @@ public abstract class MediaPreviewFragment extends Fragment {
|
||||||
protected Events events;
|
protected Events events;
|
||||||
|
|
||||||
public static MediaPreviewFragment newInstance(@NonNull Attachment attachment, boolean autoPlay) {
|
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) {
|
public static MediaPreviewFragment newInstance(@NonNull Uri dataUri, @NonNull String contentType, long size, boolean autoPlay) {
|
||||||
|
|
|
@ -98,8 +98,7 @@ public class MediaPreviewViewModel extends ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private @Nullable Media toMedia(@NonNull MediaRecord mediaRecord) {
|
private @Nullable Media toMedia(@NonNull MediaRecord mediaRecord) {
|
||||||
Uri uri = mediaRecord.getAttachment().getThumbnailUri() != null ? mediaRecord.getAttachment().getThumbnailUri()
|
Uri uri = mediaRecord.getAttachment().getUri();
|
||||||
: mediaRecord.getAttachment().getDataUri();
|
|
||||||
|
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -191,11 +191,11 @@ class MediaUploadRepository {
|
||||||
|
|
||||||
public static @NonNull Attachment asAttachment(@NonNull Context context, @NonNull Media media) {
|
public static @NonNull Attachment asAttachment(@NonNull Context context, @NonNull Media media) {
|
||||||
if (MediaUtil.isVideoType(media.getMimeType())) {
|
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())) {
|
} 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())) {
|
} 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())) {
|
} else if (MediaUtil.isTextType(media.getMimeType())) {
|
||||||
return new TextSlide(context, media.getUri(), null, media.getSize()).asAttachment();
|
return new TextSlide(context, media.getUri(), null, media.getSize()).asAttachment();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -39,7 +39,7 @@ public class ApplicationMigrations {
|
||||||
|
|
||||||
private static final int LEGACY_CANONICAL_VERSION = 455;
|
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 {
|
private static final class Version {
|
||||||
static final int LEGACY = 1;
|
static final int LEGACY = 1;
|
||||||
|
@ -60,6 +60,7 @@ public class ApplicationMigrations {
|
||||||
static final int VERSIONED_PROFILE = 16;
|
static final int VERSIONED_PROFILE = 16;
|
||||||
static final int PIN_OPT_OUT = 17;
|
static final int PIN_OPT_OUT = 17;
|
||||||
static final int TRIM_SETTINGS = 18;
|
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());
|
jobs.put(Version.TRIM_SETTINGS, new TrimByLengthSettingsMigrationJob());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lastSeenVersion < Version.THUMBNAIL_CLEANUP) {
|
||||||
|
jobs.put(Version.THUMBNAIL_CLEANUP, new DatabaseMigrationJob());
|
||||||
|
}
|
||||||
|
|
||||||
return jobs;
|
return jobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,19 +39,13 @@ public class AudioSlide extends Slide {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AudioSlide(Context context, Uri uri, long dataSize, String contentType, boolean voiceNote) {
|
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) {
|
public AudioSlide(Context context, Attachment attachment) {
|
||||||
super(context, attachment);
|
super(context, attachment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public Uri getThumbnailUri() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPlaceholder() {
|
public boolean hasPlaceholder() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -29,7 +29,7 @@ class DecryptableStreamLocalUriFetcher extends StreamLocalUriFetcher {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected InputStream loadResource(Uri uri, ContentResolver contentResolver) throws FileNotFoundException {
|
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);
|
Bitmap thumbnail = MediaUtil.getVideoThumbnail(context, uri, 1000);
|
||||||
|
|
||||||
if (thumbnail != null) {
|
if (thumbnail != null) {
|
||||||
|
|
|
@ -26,11 +26,6 @@ public class GifSlide extends ImageSlide {
|
||||||
this.borderless = borderless;
|
this.borderless = borderless;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Uri getThumbnailUri() {
|
|
||||||
return getUri();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isBorderless() {
|
public boolean isBorderless() {
|
||||||
return borderless;
|
return borderless;
|
||||||
|
|
|
@ -54,11 +54,6 @@ public class ImageSlide extends Slide {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Uri getThumbnailUri() {
|
|
||||||
return getUri();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasImage() {
|
public boolean hasImage() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -48,8 +48,8 @@ public abstract class MediaConstraints {
|
||||||
|
|
||||||
public boolean isSatisfied(@NonNull Context context, @NonNull Attachment attachment) {
|
public boolean isSatisfied(@NonNull Context context, @NonNull Attachment attachment) {
|
||||||
try {
|
try {
|
||||||
return (MediaUtil.isGif(attachment) && attachment.getSize() <= getGifMaxSize(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.getDataUri())) ||
|
(MediaUtil.isImage(attachment) && attachment.getSize() <= getImageMaxSize(context) && isWithinBounds(context, attachment.getUri())) ||
|
||||||
(MediaUtil.isAudio(attachment) && attachment.getSize() <= getAudioMaxSize(context)) ||
|
(MediaUtil.isAudio(attachment) && attachment.getSize() <= getAudioMaxSize(context)) ||
|
||||||
(MediaUtil.isVideo(attachment) && attachment.getSize() <= getVideoMaxSize(context)) ||
|
(MediaUtil.isVideo(attachment) && attachment.getSize() <= getVideoMaxSize(context)) ||
|
||||||
(MediaUtil.isFile(attachment) && attachment.getSize() <= getDocumentMaxSize(context));
|
(MediaUtil.isFile(attachment) && attachment.getSize() <= getDocumentMaxSize(context));
|
||||||
|
|
|
@ -22,24 +22,20 @@ import java.io.InputStream;
|
||||||
public class PartAuthority {
|
public class PartAuthority {
|
||||||
|
|
||||||
private static final String PART_URI_STRING = "content://org.thoughtcrime.securesms/part";
|
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 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 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 Uri STICKER_CONTENT_URI = Uri.parse(STICKER_URI_STRING);
|
||||||
|
|
||||||
private static final int PART_ROW = 1;
|
private static final int PART_ROW = 1;
|
||||||
private static final int THUMB_ROW = 2;
|
private static final int PERSISTENT_ROW = 2;
|
||||||
private static final int PERSISTENT_ROW = 3;
|
private static final int BLOB_ROW = 3;
|
||||||
private static final int BLOB_ROW = 4;
|
private static final int STICKER_ROW = 4;
|
||||||
private static final int STICKER_ROW = 5;
|
|
||||||
|
|
||||||
private static final UriMatcher uriMatcher;
|
private static final UriMatcher uriMatcher;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
uriMatcher.addURI("org.thoughtcrime.securesms", "part/*/#", PART_ROW);
|
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("org.thoughtcrime.securesms", "sticker/#", STICKER_ROW);
|
||||||
uriMatcher.addURI(DeprecatedPersistentBlobProvider.AUTHORITY, DeprecatedPersistentBlobProvider.EXPECTED_PATH_OLD, PERSISTENT_ROW);
|
uriMatcher.addURI(DeprecatedPersistentBlobProvider.AUTHORITY, DeprecatedPersistentBlobProvider.EXPECTED_PATH_OLD, PERSISTENT_ROW);
|
||||||
uriMatcher.addURI(DeprecatedPersistentBlobProvider.AUTHORITY, DeprecatedPersistentBlobProvider.EXPECTED_PATH_NEW, 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)
|
public static InputStream getAttachmentThumbnailStream(@NonNull Context context, @NonNull Uri uri)
|
||||||
throws IOException
|
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);
|
return getAttachmentStream(context, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +55,6 @@ public class PartAuthority {
|
||||||
try {
|
try {
|
||||||
switch (match) {
|
switch (match) {
|
||||||
case PART_ROW: return DatabaseFactory.getAttachmentDatabase(context).getAttachmentStream(new PartUriParser(uri).getPartId(), 0);
|
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 STICKER_ROW: return DatabaseFactory.getStickerDatabase(context).getStickerStream(ContentUris.parseId(uri));
|
||||||
case PERSISTENT_ROW: return DeprecatedPersistentBlobProvider.getInstance(context).getStream(context, ContentUris.parseId(uri));
|
case PERSISTENT_ROW: return DeprecatedPersistentBlobProvider.getInstance(context).getStream(context, ContentUris.parseId(uri));
|
||||||
case BLOB_ROW: return BlobProvider.getInstance().getStream(context, uri);
|
case BLOB_ROW: return BlobProvider.getInstance().getStream(context, uri);
|
||||||
|
@ -81,7 +69,6 @@ public class PartAuthority {
|
||||||
int match = uriMatcher.match(uri);
|
int match = uriMatcher.match(uri);
|
||||||
|
|
||||||
switch (match) {
|
switch (match) {
|
||||||
case THUMB_ROW:
|
|
||||||
case PART_ROW:
|
case PART_ROW:
|
||||||
Attachment attachment = DatabaseFactory.getAttachmentDatabase(context).getAttachment(new PartUriParser(uri).getPartId());
|
Attachment attachment = DatabaseFactory.getAttachmentDatabase(context).getAttachment(new PartUriParser(uri).getPartId());
|
||||||
|
|
||||||
|
@ -100,7 +87,6 @@ public class PartAuthority {
|
||||||
int match = uriMatcher.match(uri);
|
int match = uriMatcher.match(uri);
|
||||||
|
|
||||||
switch (match) {
|
switch (match) {
|
||||||
case THUMB_ROW:
|
|
||||||
case PART_ROW:
|
case PART_ROW:
|
||||||
Attachment attachment = DatabaseFactory.getAttachmentDatabase(context).getAttachment(new PartUriParser(uri).getPartId());
|
Attachment attachment = DatabaseFactory.getAttachmentDatabase(context).getAttachment(new PartUriParser(uri).getPartId());
|
||||||
|
|
||||||
|
@ -119,7 +105,6 @@ public class PartAuthority {
|
||||||
int match = uriMatcher.match(uri);
|
int match = uriMatcher.match(uri);
|
||||||
|
|
||||||
switch (match) {
|
switch (match) {
|
||||||
case THUMB_ROW:
|
|
||||||
case PART_ROW:
|
case PART_ROW:
|
||||||
Attachment attachment = DatabaseFactory.getAttachmentDatabase(context).getAttachment(new PartUriParser(uri).getPartId());
|
Attachment attachment = DatabaseFactory.getAttachmentDatabase(context).getAttachment(new PartUriParser(uri).getPartId());
|
||||||
|
|
||||||
|
@ -145,8 +130,7 @@ public class PartAuthority {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Uri getAttachmentThumbnailUri(AttachmentId attachmentId) {
|
public static Uri getAttachmentThumbnailUri(AttachmentId attachmentId) {
|
||||||
Uri uri = Uri.withAppendedPath(THUMB_CONTENT_URI, String.valueOf(attachmentId.getUniqueId()));
|
return getAttachmentDataUri(attachmentId);
|
||||||
return ContentUris.withAppendedId(uri, attachmentId.getRowId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Uri getStickerUri(long id) {
|
public static Uri getStickerUri(long id) {
|
||||||
|
@ -157,11 +141,18 @@ public class PartAuthority {
|
||||||
int match = uriMatcher.match(uri);
|
int match = uriMatcher.match(uri);
|
||||||
switch (match) {
|
switch (match) {
|
||||||
case PART_ROW:
|
case PART_ROW:
|
||||||
case THUMB_ROW:
|
|
||||||
case PERSISTENT_ROW:
|
case PERSISTENT_ROW:
|
||||||
case BLOB_ROW:
|
case BLOB_ROW:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,12 +52,7 @@ public abstract class Slide {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Uri getUri() {
|
public Uri getUri() {
|
||||||
return attachment.getDataUri();
|
return attachment.getUri();
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Uri getThumbnailUri() {
|
|
||||||
return attachment.getThumbnailUri();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -188,7 +183,6 @@ public abstract class Slide {
|
||||||
String resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri)).or(defaultMime);
|
String resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri)).or(defaultMime);
|
||||||
String fastPreflightId = String.valueOf(new SecureRandom().nextLong());
|
String fastPreflightId = String.valueOf(new SecureRandom().nextLong());
|
||||||
return new UriAttachment(uri,
|
return new UriAttachment(uri,
|
||||||
hasThumbnail ? uri : null,
|
|
||||||
resolvedType,
|
resolvedType,
|
||||||
AttachmentDatabase.TRANSFER_PROGRESS_STARTED,
|
AttachmentDatabase.TRANSFER_PROGRESS_STARTED,
|
||||||
size,
|
size,
|
||||||
|
@ -246,13 +240,12 @@ public abstract class Slide {
|
||||||
this.hasImage() == that.hasImage() &&
|
this.hasImage() == that.hasImage() &&
|
||||||
this.hasVideo() == that.hasVideo() &&
|
this.hasVideo() == that.hasVideo() &&
|
||||||
this.getTransferState() == that.getTransferState() &&
|
this.getTransferState() == that.getTransferState() &&
|
||||||
Util.equals(this.getUri(), that.getUri()) &&
|
Util.equals(this.getUri(), that.getUri());
|
||||||
Util.equals(this.getThumbnailUri(), that.getThumbnailUri());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Util.hashCode(getContentType(), hasAudio(), hasImage(),
|
return Util.hashCode(getContentType(), hasAudio(), hasImage(),
|
||||||
hasVideo(), getUri(), getThumbnailUri(), getTransferState());
|
hasVideo(), getUri(), getTransferState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,11 +37,6 @@ public class StickerSlide extends Slide {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Uri getThumbnailUri() {
|
|
||||||
return getUri();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasSticker() {
|
public boolean hasSticker() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -37,7 +37,11 @@ public class VideoSlide extends Slide {
|
||||||
}
|
}
|
||||||
|
|
||||||
public VideoSlide(Context context, Uri uri, long dataSize, @Nullable String caption, @Nullable AttachmentDatabase.TransformProperties transformProperties) {
|
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) {
|
public VideoSlide(Context context, Attachment attachment) {
|
||||||
|
|
|
@ -344,8 +344,8 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Optional<Uri> getThumbnailUri(@Nullable Slide slide) {
|
private static Optional<Uri> getThumbnailUri(@Nullable Slide slide) {
|
||||||
if (slide != null && !slide.isInProgress() && slide.getThumbnailUri() != null) {
|
if (slide != null && !slide.isInProgress() && slide.getUri() != null) {
|
||||||
return Optional.of(slide.getThumbnailUri());
|
return Optional.of(slide.getUri());
|
||||||
} else {
|
} else {
|
||||||
return Optional.absent();
|
return Optional.absent();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,15 @@ import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
import com.bumptech.glide.load.resource.gif.GifDrawable;
|
import com.bumptech.glide.load.resource.gif.GifDrawable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
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.logging.Log;
|
||||||
import org.thoughtcrime.securesms.mms.AudioSlide;
|
import org.thoughtcrime.securesms.mms.AudioSlide;
|
||||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||||
|
@ -152,7 +155,7 @@ public class MediaUtil {
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static Pair<Integer, Integer> getDimensions(@NonNull Context context, @Nullable String contentType, @Nullable Uri uri) {
|
public static Pair<Integer, Integer> 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);
|
return new Pair<>(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,18 +164,24 @@ public class MediaUtil {
|
||||||
if (MediaUtil.isGif(contentType)) {
|
if (MediaUtil.isGif(contentType)) {
|
||||||
try {
|
try {
|
||||||
GifDrawable drawable = GlideApp.with(context)
|
GifDrawable drawable = GlideApp.with(context)
|
||||||
.asGif()
|
.asGif()
|
||||||
.skipMemoryCache(true)
|
.skipMemoryCache(true)
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
.load(new DecryptableUri(uri))
|
.load(new DecryptableUri(uri))
|
||||||
.submit()
|
.submit()
|
||||||
.get();
|
.get();
|
||||||
dimens = new Pair<>(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
|
dimens = new Pair<>(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Log.w(TAG, "Was unable to complete work for GIF dimensions.", e);
|
Log.w(TAG, "Was unable to complete work for GIF dimensions.", e);
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
Log.w(TAG, "Glide experienced an exception while trying to get GIF dimensions.", 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 {
|
} else {
|
||||||
InputStream attachmentStream = null;
|
InputStream attachmentStream = null;
|
||||||
try {
|
try {
|
||||||
|
@ -297,12 +306,16 @@ public class MediaUtil {
|
||||||
return (null != contentType) && contentType.equals(VIEW_ONCE);
|
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) {
|
if (BlobProvider.isAuthority(uri) && MediaUtil.isVideo(BlobProvider.getMimeType(uri)) && Build.VERSION.SDK_INT >= 23) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uri == null || !isSupportedVideoUriScheme(uri.getScheme())) {
|
if (!isSupportedVideoUriScheme(uri.getScheme())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,14 +326,18 @@ public class MediaUtil {
|
||||||
} else if (uri.toString().startsWith("file://") &&
|
} else if (uri.toString().startsWith("file://") &&
|
||||||
MediaUtil.isVideo(URLConnection.guessContentTypeFromName(uri.toString()))) {
|
MediaUtil.isVideo(URLConnection.guessContentTypeFromName(uri.toString()))) {
|
||||||
return true;
|
return true;
|
||||||
|
} else if (PartAuthority.isAttachmentUri(uri) && MediaUtil.isVideoType(PartAuthority.getAttachmentContentType(context, uri))) {
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static @Nullable Bitmap getVideoThumbnail(Context context, Uri uri, long timeUs) {
|
public static @Nullable Bitmap getVideoThumbnail(@NonNull Context context, @Nullable Uri uri, long timeUs) {
|
||||||
if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
|
if (uri == null) {
|
||||||
|
return null;
|
||||||
|
} else if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
|
||||||
long videoId = Long.parseLong(uri.getLastPathSegment().split(":")[1]);
|
long videoId = Long.parseLong(uri.getLastPathSegment().split(":")[1]);
|
||||||
|
|
||||||
return MediaStore.Video.Thumbnails.getThumbnail(context.getContentResolver(),
|
return MediaStore.Video.Thumbnails.getThumbnail(context.getContentResolver(),
|
||||||
|
@ -338,24 +355,44 @@ public class MediaUtil {
|
||||||
MediaUtil.isVideo(URLConnection.guessContentTypeFromName(uri.toString()))) {
|
MediaUtil.isVideo(URLConnection.guessContentTypeFromName(uri.toString()))) {
|
||||||
return ThumbnailUtils.createVideoThumbnail(uri.toString().replace("file://", ""),
|
return ThumbnailUtils.createVideoThumbnail(uri.toString().replace("file://", ""),
|
||||||
MediaStore.Video.Thumbnails.MINI_KIND);
|
MediaStore.Video.Thumbnails.MINI_KIND);
|
||||||
} else if (BlobProvider.isAuthority(uri) &&
|
} else if (Build.VERSION.SDK_INT >= 23 &&
|
||||||
MediaUtil.isVideo(BlobProvider.getMimeType(uri)) &&
|
BlobProvider.isAuthority(uri) &&
|
||||||
Build.VERSION.SDK_INT >= 23) {
|
MediaUtil.isVideo(BlobProvider.getMimeType(uri)))
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
MediaDataSource mediaDataSource = BlobProvider.getInstance().getMediaDataSource(context, uri);
|
MediaDataSource source = BlobProvider.getInstance().getMediaDataSource(context, uri);
|
||||||
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
|
return extractFrame(source, timeUs);
|
||||||
|
|
||||||
MediaMetadataRetrieverUtil.setDataSource(mediaMetadataRetriever, mediaDataSource);
|
|
||||||
return mediaMetadataRetriever.getFrameAtTime(timeUs);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, "failed to get thumbnail for video blob uri: " + uri, e);
|
Log.w(TAG, "Failed to extract frame for URI: " + uri, e);
|
||||||
return null;
|
}
|
||||||
|
} 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;
|
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) {
|
public static @Nullable String getDiscreteMimeType(@NonNull String mimeType) {
|
||||||
final String[] sections = mimeType.split("/", 2);
|
final String[] sections = mimeType.split("/", 2);
|
||||||
return sections.length > 1 ? sections[0] : null;
|
return sections.length > 1 ? sections[0] : null;
|
||||||
|
|
Loading…
Add table
Reference in a new issue