diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java index d54b260432..3e7dcdc4b6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java @@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.blurhash.BlurHash; import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.stickers.StickerLocator; +import org.thoughtcrime.securesms.util.FeatureFlags; import java.util.Comparator; @@ -60,7 +61,7 @@ public class DatabaseAttachment extends Attachment { @Override @Nullable public Uri getUri() { - if (hasData) { + if (hasData || (FeatureFlags.instantVideoPlayback() && getIncrementalDigest() != null)) { return PartAuthority.getAttachmentDataUri(attachmentId); } else { return null; diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.kt b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.kt index 67f97895e9..7ee62e28cb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.kt @@ -255,6 +255,14 @@ class ConversationItemThumbnail @JvmOverloads constructor( state.applyState(thumbnail, album) } + fun setProgressWheelClickListener(listener: SlideClickListener?) { + state = state.copy( + thumbnailViewState = state.thumbnailViewState.copy(progressWheelClickListener = listener) + ) + + state.applyState(thumbnail, album) + } + private fun setThumbnailBounds(bounds: IntArray) { val (minWidth, maxWidth, minHeight, maxHeight) = bounds state = state.copy( diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnailState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnailState.kt index 600efa2726..f8ef85a79a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnailState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnailState.kt @@ -31,6 +31,8 @@ data class ConversationItemThumbnailState( @IgnoredOnParcel private val downloadClickListener: SlidesClickedListener? = null, @IgnoredOnParcel + private val progressWheelClickListener: SlideClickListener? = null, + @IgnoredOnParcel private val longClickListener: OnLongClickListener? = null, private val visibility: Int = View.GONE, private val minWidth: Int = -1, @@ -55,6 +57,7 @@ data class ConversationItemThumbnailState( thumbnailView.get().setRadii(cornerTopLeft, cornerTopRight, cornerBottomRight, cornerBottomLeft) thumbnailView.get().setThumbnailClickListener(clickListener) thumbnailView.get().setDownloadClickListener(downloadClickListener) + thumbnailView.get().setProgressWheelClickListener(progressWheelClickListener) thumbnailView.get().setOnLongClickListener(longClickListener) thumbnailView.get().setBounds(minWidth, maxWidth, minHeight, maxHeight) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java index ff785b87f3..b20a2a6096 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java @@ -41,6 +41,7 @@ import org.thoughtcrime.securesms.mms.SlideClickListener; import org.thoughtcrime.securesms.mms.SlidesClickedListener; import org.thoughtcrime.securesms.mms.VideoSlide; import org.thoughtcrime.securesms.stories.StoryTextPostModel; +import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; @@ -79,11 +80,12 @@ public class ThumbnailView extends FrameLayout { private final CornerMask cornerMask; - private ThumbnailViewTransferControlsState transferControlsState = new ThumbnailViewTransferControlsState(); + private ThumbnailViewTransferControlsState transferControlsState = new ThumbnailViewTransferControlsState(); private Stub transferControlViewStub; - private SlideClickListener thumbnailClickListener = null; - private SlidesClickedListener downloadClickListener = null; - private Slide slide = null; + private SlideClickListener thumbnailClickListener = null; + private SlidesClickedListener downloadClickListener = null; + private SlideClickListener progressWheelClickListener = null; + private Slide slide = null; public ThumbnailView(Context context) { @@ -366,6 +368,11 @@ public class ThumbnailView extends FrameLayout { transferControlsState = transferControlsState.withSlide(slide) .withDownloadClickListener(new DownloadClickDispatcher()); + + if (FeatureFlags.instantVideoPlayback()) { + transferControlsState = transferControlsState.withProgressWheelClickListener(new ProgressWheelClickDispatcher()); + } + transferControlsState.applyState(transferControlViewStub); } else { transferControlViewStub.setVisibility(View.GONE); @@ -518,6 +525,10 @@ public class ThumbnailView extends FrameLayout { this.downloadClickListener = listener; } + public void setProgressWheelClickListener(SlideClickListener listener) { + this.progressWheelClickListener = listener; + } + public void clear(GlideRequests glideRequests) { glideRequests.clear(image); image.setImageDrawable(null); @@ -659,6 +670,18 @@ public class ThumbnailView extends FrameLayout { } } + private class ProgressWheelClickDispatcher implements View.OnClickListener { + @Override + public void onClick(View view) { + Log.i(TAG, "onClick() for progress wheel"); + if (progressWheelClickListener != null && slide != null) { + progressWheelClickListener.onClick(view, slide); + } else { + Log.w(TAG, "Received a progress wheel click, but unable to execute it. slide: " + slide + " progressWheelClickListener: " + progressWheelClickListener); + } + } + } + private static class BlurHashClearListener implements ListenableFuture.Listener { private final GlideRequests glideRequests; diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailViewTransferControlsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailViewTransferControlsState.kt index 9973fee2df..c8d60ea185 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailViewTransferControlsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailViewTransferControlsState.kt @@ -12,6 +12,7 @@ data class ThumbnailViewTransferControlsState( val isClickable: Boolean = true, val slide: Slide? = null, val downloadClickedListener: OnClickListener? = null, + val progressWheelClickedListener: OnClickListener? = null, val showDownloadText: Boolean = true ) { @@ -19,6 +20,7 @@ data class ThumbnailViewTransferControlsState( fun withClickable(isClickable: Boolean): ThumbnailViewTransferControlsState = copy(isClickable = isClickable) fun withSlide(slide: Slide?): ThumbnailViewTransferControlsState = copy(slide = slide) fun withDownloadClickListener(downloadClickedListener: OnClickListener): ThumbnailViewTransferControlsState = copy(downloadClickedListener = downloadClickedListener) + fun withProgressWheelClickListener(progressWheelClickedListener: OnClickListener): ThumbnailViewTransferControlsState = copy(progressWheelClickedListener = progressWheelClickedListener) fun withDownloadText(showDownloadText: Boolean): ThumbnailViewTransferControlsState = copy(showDownloadText = showDownloadText) fun applyState(transferControlView: Stub) { @@ -29,6 +31,7 @@ data class ThumbnailViewTransferControlsState( transferControlView.get().setSlide(slide) } transferControlView.get().setDownloadClickListener(downloadClickedListener) + transferControlView.get().setProgressWheelClickListener(progressWheelClickedListener) transferControlView.get().setShowDownloadText(showDownloadText) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/TransferControlView.java b/app/src/main/java/org/thoughtcrime/securesms/components/TransferControlView.java index 6d66a26a38..e60ef8b919 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/TransferControlView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/TransferControlView.java @@ -30,6 +30,7 @@ import java.util.Map; public final class TransferControlView extends FrameLayout { + private static final String TAG = "TransferControlView"; private static final int UPLOAD_TASK_WEIGHT = 1; /** @@ -152,6 +153,10 @@ public final class TransferControlView extends FrameLayout { downloadDetails.setOnClickListener(listener); } + public void setProgressWheelClickListener(final @Nullable OnClickListener listener) { + progressWheel.setOnClickListener(listener); + } + public void clear() { clearAnimation(); setVisibility(GONE); @@ -247,13 +252,14 @@ public final class TransferControlView extends FrameLayout { @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEventAsync(final PartProgressEvent event) { - if (networkProgress.containsKey(event.attachment)) { + final Attachment attachment = event.attachment; + if (networkProgress.containsKey(attachment)) { float proportionCompleted = ((float) event.progress) / event.total; if (event.type == PartProgressEvent.Type.COMPRESSION) { - compresssionProgress.put(event.attachment, proportionCompleted); + compresssionProgress.put(attachment, proportionCompleted); } else { - networkProgress.put(event.attachment, proportionCompleted); + networkProgress.put(attachment, proportionCompleted); } progressWheel.setInstantProgress(calculateProgress(networkProgress, compresssionProgress)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java index 9485752d47..4dfffb7b1e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -135,6 +135,7 @@ import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.InterceptableLongClickCopyLinkSpan; import org.thoughtcrime.securesms.util.LongClickMovementMethod; +import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MessageRecordUtil; import org.thoughtcrime.securesms.util.PlaceholderURLSpan; import org.thoughtcrime.securesms.util.Projection; @@ -240,6 +241,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo private final PassthroughClickListener passthroughClickListener = new PassthroughClickListener(); private final AttachmentDownloadClickListener downloadClickListener = new AttachmentDownloadClickListener(); + private final ProgressWheelClickListener progressWheelClickListener = new ProgressWheelClickListener(); private final SlideClickPassthroughListener singleDownloadClickListener = new SlideClickPassthroughListener(downloadClickListener); private final SharedContactEventListener sharedContactEventListener = new SharedContactEventListener(); private final SharedContactClickListener sharedContactClickListener = new SharedContactClickListener(); @@ -1162,6 +1164,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo mediaThumbnailStub.require().setImageResource(glideRequests, Collections.singletonList(new ImageSlide(linkPreview.getThumbnail().get())), showControls, false); mediaThumbnailStub.require().setThumbnailClickListener(new LinkPreviewThumbnailClickListener()); mediaThumbnailStub.require().setDownloadClickListener(downloadClickListener); + mediaThumbnailStub.require().setProgressWheelClickListener(progressWheelClickListener); mediaThumbnailStub.require().setOnLongClickListener(passthroughClickListener); linkPreviewStub.get().setLinkPreview(glideRequests, linkPreview, false); @@ -1301,6 +1304,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo false); mediaThumbnailStub.require().setThumbnailClickListener(new ThumbnailClickListener()); mediaThumbnailStub.require().setDownloadClickListener(downloadClickListener); + mediaThumbnailStub.require().setProgressWheelClickListener(progressWheelClickListener); mediaThumbnailStub.require().setOnLongClickListener(passthroughClickListener); mediaThumbnailStub.require().setOnClickListener(passthroughClickListener); mediaThumbnailStub.require().showShade(messageRecord.isDisplayBodyEmpty(getContext()) && !hasExtraText(messageRecord)); @@ -1545,6 +1549,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo messageBody.setSpan(new MentionClickableSpan(RecipientId.from(annotation.getValue())), messageBody.getSpanStart(annotation), messageBody.getSpanEnd(annotation), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } + private void setStatusIcons(MessageRecord messageRecord, boolean hasWallpaper) { bodyText.setCompoundDrawablesWithIntrinsicBounds(0, 0, messageRecord.isKeyExchange() ? R.drawable.ic_menu_login : 0, 0); @@ -2429,6 +2434,20 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } } + private class ProgressWheelClickListener implements SlideClickListener { + + @Override + public void onClick(View v, Slide slide) { + final boolean isIncremental = slide.asAttachment().getIncrementalDigest() != null; + final boolean contentTypeSupported = MediaUtil.isVideoType(slide.getContentType()); + if (FeatureFlags.instantVideoPlayback() && isIncremental && contentTypeSupported) { + launchMediaPreview(v, slide); + } else { + Log.d(TAG, "Non-eligible slide clicked: " + "\tisIncremental: " + isIncremental + "\tcontentTypeSupported: " + contentTypeSupported); + } + } + } + private class SlideClickPassthroughListener implements SlideClickListener { private final SlidesClickedListener original; @@ -2462,34 +2481,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } else if (!canPlayContent && mediaItem != null && eventListener != null) { eventListener.onPlayInlineContent(conversationMessage); } else if (MediaPreviewV2Fragment.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) { - if (eventListener == null) { - return; - } - - MediaIntentFactory.MediaPreviewArgs args = new MediaIntentFactory.MediaPreviewArgs( - messageRecord.getThreadId(), - messageRecord.getTimestamp(), - slide.getUri(), - slide.getContentType(), - slide.asAttachment().getSize(), - slide.getCaption().orElse(null), - false, - false, - false, - false, - MediaTable.Sorting.Newest, - slide.isVideoGif(), - new MediaIntentFactory.SharedElementArgs( - slide.asAttachment().getWidth(), - slide.asAttachment().getHeight(), - mediaThumbnailStub.require().getCorners().getTopLeft(), - mediaThumbnailStub.require().getCorners().getTopRight(), - mediaThumbnailStub.require().getCorners().getBottomRight(), - mediaThumbnailStub.require().getCorners().getBottomLeft() - ), - false); - MediaPreviewCache.INSTANCE.setDrawable(((ThumbnailView) v).getImageDrawable()); - eventListener.goToMediaPreview(ConversationItem.this, v, args); + launchMediaPreview(v, slide); } else if (slide.getUri() != null) { Log.i(TAG, "Clicked: " + slide.getUri() + " , " + slide.getContentType()); Uri publicUri = PartAuthority.getAttachmentPublicUri(slide.getUri()); @@ -2526,6 +2518,47 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } } + private void launchMediaPreview(View v, Slide slide) { + if (eventListener == null) { + Log.w(TAG, "Could not launch media preview for item: eventListener was null"); + return; + } + + Uri mediaUri = slide.getUri(); + + if (mediaUri == null) { + Log.w(TAG, "Could not launch media preview for item: uri was null"); + return; + } + + MediaIntentFactory.MediaPreviewArgs args = new MediaIntentFactory.MediaPreviewArgs( + messageRecord.getThreadId(), + messageRecord.getTimestamp(), + mediaUri, + slide.getContentType(), + slide.asAttachment().getSize(), + slide.getCaption().orElse(null), + false, + false, + false, + false, + MediaTable.Sorting.Newest, + slide.isVideoGif(), + new MediaIntentFactory.SharedElementArgs( + slide.asAttachment().getWidth(), + slide.asAttachment().getHeight(), + mediaThumbnailStub.require().getCorners().getTopLeft(), + mediaThumbnailStub.require().getCorners().getTopRight(), + mediaThumbnailStub.require().getCorners().getBottomRight(), + mediaThumbnailStub.require().getCorners().getBottomLeft() + ), + false); + if (v instanceof ThumbnailView) { + MediaPreviewCache.INSTANCE.setDrawable(((ThumbnailView) v).getImageDrawable()); + } + eventListener.goToMediaPreview(ConversationItem.this, v, args); + } + private class PassthroughClickListener implements View.OnLongClickListener, View.OnClickListener { @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.java b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.java index e7ccf9b1ad..cb63c9b80b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.java @@ -1343,39 +1343,39 @@ public class AttachmentTable extends DatabaseTable { private @NonNull DatabaseAttachment getAttachment(@NonNull Cursor cursor) { String contentType = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_TYPE)); return new DatabaseAttachment(new AttachmentId(cursor.getLong(cursor.getColumnIndexOrThrow(ROW_ID)), - cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID))), - cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)), - !cursor.isNull(cursor.getColumnIndexOrThrow(DATA)), - MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType), - contentType, - cursor.getInt(cursor.getColumnIndexOrThrow(TRANSFER_STATE)), - cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)), - cursor.getString(cursor.getColumnIndexOrThrow(FILE_NAME)), - cursor.getInt(cursor.getColumnIndexOrThrow(CDN_NUMBER)), - cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)), - cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_DISPOSITION)), - cursor.getString(cursor.getColumnIndexOrThrow(NAME)), - cursor.getBlob(cursor.getColumnIndexOrThrow(DIGEST)), - cursor.getBlob(cursor.getColumnIndexOrThrow(MAC_DIGEST)), - cursor.getString(cursor.getColumnIndexOrThrow(FAST_PREFLIGHT_ID)), - cursor.getInt(cursor.getColumnIndexOrThrow(VOICE_NOTE)) == 1, - cursor.getInt(cursor.getColumnIndexOrThrow(BORDERLESS)) == 1, - cursor.getInt(cursor.getColumnIndexOrThrow(VIDEO_GIF)) == 1, - cursor.getInt(cursor.getColumnIndexOrThrow(WIDTH)), - cursor.getInt(cursor.getColumnIndexOrThrow(HEIGHT)), - cursor.getInt(cursor.getColumnIndexOrThrow(QUOTE)) == 1, - cursor.getString(cursor.getColumnIndexOrThrow(CAPTION)), - cursor.getInt(cursor.getColumnIndexOrThrow(STICKER_ID)) >= 0 - ? new StickerLocator(CursorUtil.requireString(cursor, STICKER_PACK_ID), - CursorUtil.requireString(cursor, STICKER_PACK_KEY), - CursorUtil.requireInt(cursor, STICKER_ID), - CursorUtil.requireString(cursor, STICKER_EMOJI)) - : null, - MediaUtil.isAudioType(contentType) ? null : BlurHash.parseOrNull(cursor.getString(cursor.getColumnIndexOrThrow(VISUAL_HASH))), - MediaUtil.isAudioType(contentType) ? AudioHash.parseOrNull(cursor.getString(cursor.getColumnIndexOrThrow(VISUAL_HASH))) : null, - TransformProperties.parse(cursor.getString(cursor.getColumnIndexOrThrow(TRANSFORM_PROPERTIES))), - cursor.getInt(cursor.getColumnIndexOrThrow(DISPLAY_ORDER)), - cursor.getLong(cursor.getColumnIndexOrThrow(UPLOAD_TIMESTAMP))); + cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID))), + cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)), + !cursor.isNull(cursor.getColumnIndexOrThrow(DATA)), + MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType), + contentType, + cursor.getInt(cursor.getColumnIndexOrThrow(TRANSFER_STATE)), + cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)), + cursor.getString(cursor.getColumnIndexOrThrow(FILE_NAME)), + cursor.getInt(cursor.getColumnIndexOrThrow(CDN_NUMBER)), + cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)), + cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_DISPOSITION)), + cursor.getString(cursor.getColumnIndexOrThrow(NAME)), + cursor.getBlob(cursor.getColumnIndexOrThrow(DIGEST)), + cursor.getBlob(cursor.getColumnIndexOrThrow(MAC_DIGEST)), + cursor.getString(cursor.getColumnIndexOrThrow(FAST_PREFLIGHT_ID)), + cursor.getInt(cursor.getColumnIndexOrThrow(VOICE_NOTE)) == 1, + cursor.getInt(cursor.getColumnIndexOrThrow(BORDERLESS)) == 1, + cursor.getInt(cursor.getColumnIndexOrThrow(VIDEO_GIF)) == 1, + cursor.getInt(cursor.getColumnIndexOrThrow(WIDTH)), + cursor.getInt(cursor.getColumnIndexOrThrow(HEIGHT)), + cursor.getInt(cursor.getColumnIndexOrThrow(QUOTE)) == 1, + cursor.getString(cursor.getColumnIndexOrThrow(CAPTION)), + cursor.getInt(cursor.getColumnIndexOrThrow(STICKER_ID)) >= 0 + ? new StickerLocator(CursorUtil.requireString(cursor, STICKER_PACK_ID), + CursorUtil.requireString(cursor, STICKER_PACK_KEY), + CursorUtil.requireInt(cursor, STICKER_ID), + CursorUtil.requireString(cursor, STICKER_EMOJI)) + : null, + MediaUtil.isAudioType(contentType) ? null : BlurHash.parseOrNull(cursor.getString(cursor.getColumnIndexOrThrow(VISUAL_HASH))), + MediaUtil.isAudioType(contentType) ? AudioHash.parseOrNull(cursor.getString(cursor.getColumnIndexOrThrow(VISUAL_HASH))) : null, + TransformProperties.parse(cursor.getString(cursor.getColumnIndexOrThrow(TRANSFORM_PROPERTIES))), + cursor.getInt(cursor.getColumnIndexOrThrow(DISPLAY_ORDER)), + cursor.getLong(cursor.getColumnIndexOrThrow(UPLOAD_TIMESTAMP))); } private AttachmentId insertAttachment(long mmsId, Attachment attachment, boolean quote) @@ -1514,15 +1514,28 @@ public class AttachmentTable extends DatabaseTable { @RequiresApi(23) - public @Nullable MediaDataSource mediaDataSourceFor(@NonNull AttachmentId attachmentId) { + public @Nullable MediaDataSource mediaDataSourceFor(@NonNull AttachmentId attachmentId, Boolean allowReadingFromTempFile) { DataInfo dataInfo = getAttachmentDataFileInfo(attachmentId, DATA); - if (dataInfo == null) { - Log.w(TAG, "No data file found for video attachment..."); - return null; + if (dataInfo != null) { + return EncryptedMediaDataSource.createFor(attachmentSecret, dataInfo.file, dataInfo.random, dataInfo.length); } - return EncryptedMediaDataSource.createFor(attachmentSecret, dataInfo.file, dataInfo.random, dataInfo.length); + if (allowReadingFromTempFile) { + Log.d(TAG, "Completed data file not found for video attachment, checking for in-progress files."); + + SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); + + File transferFile = getTransferFile(database, attachmentId); + + if (transferFile != null) { + return EncryptedMediaDataSource.createForDiskBlob(attachmentSecret, transferFile); + } + } + + Log.w(TAG, "No data file found for video attachment!"); + + return null; } public void duplicateAttachmentsForMessage(long destinationMessageId, long sourceMessageId, Collection excludedIds) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt index cd0c0d6542..662eb45405 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt @@ -8,6 +8,7 @@ import org.signal.core.util.requireLong import org.signal.core.util.requireNonNullString import org.thoughtcrime.securesms.attachments.DatabaseAttachment import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.util.FeatureFlags import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.MediaUtil.SlideType @@ -68,8 +69,7 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD ) AND (%s) AND ${MessageTable.VIEW_ONCE} = 0 AND - ${MessageTable.STORY_TYPE} = 0 AND - ${AttachmentTable.DATA} IS NOT NULL AND + ${MessageTable.STORY_TYPE} = 0 AND ${MessageTable.LATEST_REVISION_ID} IS NULL AND ( ${AttachmentTable.QUOTE} = 0 OR @@ -98,32 +98,54 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD private val GALLERY_MEDIA_QUERY = String.format( BASE_MEDIA_QUERY, """ + ${AttachmentTable.DATA} IS NOT NULL AND ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'image/svg%' AND (${AttachmentTable.CONTENT_TYPE} LIKE 'image/%' OR ${AttachmentTable.CONTENT_TYPE} LIKE 'video/%') """ ) - private val AUDIO_MEDIA_QUERY = String.format(BASE_MEDIA_QUERY, "${AttachmentTable.CONTENT_TYPE} LIKE 'audio/%'") + private val GALLERY_MEDIA_QUERY_INCLUDING_TEMP_VIDEOS = String.format( + BASE_MEDIA_QUERY, + """ + ${AttachmentTable.DATA} IS NOT NULL OR (${AttachmentTable.CONTENT_TYPE} LIKE 'video/%' AND ${AttachmentTable.MAC_DIGEST} IS NOT NULL) AND + ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'image/svg%' AND + (${AttachmentTable.CONTENT_TYPE} LIKE 'image/%' OR ${AttachmentTable.CONTENT_TYPE} LIKE 'video/%') + """ + ) + + private val AUDIO_MEDIA_QUERY = String.format( + BASE_MEDIA_QUERY, + """ + ${AttachmentTable.DATA} IS NOT NULL AND + ${AttachmentTable.CONTENT_TYPE} LIKE 'audio/%' + """ + ) private val ALL_MEDIA_QUERY = String.format(BASE_MEDIA_QUERY, "${AttachmentTable.CONTENT_TYPE} NOT LIKE 'text/x-signal-plain'") private val DOCUMENT_MEDIA_QUERY = String.format( BASE_MEDIA_QUERY, """ - ${AttachmentTable.CONTENT_TYPE} LIKE 'image/svg%' OR + ${AttachmentTable.DATA} IS NOT NULL AND ( - ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'image/%' AND - ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'video/%' AND - ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'audio/%' AND - ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'text/x-signal-plain' + ${AttachmentTable.CONTENT_TYPE} LIKE 'image/svg%' OR + ( + ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'image/%' AND + ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'video/%' AND + ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'audio/%' AND + ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'text/x-signal-plain' + ) )""" ) - private fun applyEqualityOperator(threadId: Long, query: String): String { return query.replace("__EQUALITY__", if (threadId == ALL_THREADS.toLong()) "!=" else "=") } } fun getGalleryMediaForThread(threadId: Long, sorting: Sorting): Cursor { - val query = sorting.applyToQuery(applyEqualityOperator(threadId, GALLERY_MEDIA_QUERY)) + val query = if (FeatureFlags.instantVideoPlayback()) { + sorting.applyToQuery(applyEqualityOperator(threadId, GALLERY_MEDIA_QUERY_INCLUDING_TEMP_VIDEOS)) + } else { + sorting.applyToQuery(applyEqualityOperator(threadId, GALLERY_MEDIA_QUERY)) + } val args = arrayOf(threadId.toString() + "") return readableDatabase.rawQuery(query, args) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java index 05e7c43a19..a8587e9503 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java @@ -209,7 +209,7 @@ public final class AttachmentCompressionJob extends BaseJob { notification.setIndeterminateProgress(); - try (MediaDataSource dataSource = attachmentDatabase.mediaDataSourceFor(attachment.getAttachmentId())) { + try (MediaDataSource dataSource = attachmentDatabase.mediaDataSourceFor(attachment.getAttachmentId(), false)) { if (dataSource == null) { throw new UndeliverableMessageException("Cannot get media data source for attachment."); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/media/DecryptableUriMediaInput.java b/app/src/main/java/org/thoughtcrime/securesms/media/DecryptableUriMediaInput.java index 255589c5fd..bc8f984f66 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/media/DecryptableUriMediaInput.java +++ b/app/src/main/java/org/thoughtcrime/securesms/media/DecryptableUriMediaInput.java @@ -41,7 +41,7 @@ public final class DecryptableUriMediaInput { throw new AssertionError(); } - MediaDataSource mediaDataSource = SignalDatabase.attachments().mediaDataSourceFor(partId); + MediaDataSource mediaDataSource = SignalDatabase.attachments().mediaDataSourceFor(partId, true); if (mediaDataSource == null) { throw new AssertionError(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt index 7278b1c556..496deaccf9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt @@ -66,9 +66,7 @@ class MediaPreviewRepository { for (i in 0..limit) { val element = MediaTable.MediaRecord.from(cursor) - if (element != null) { - mediaRecords.add(element) - } + mediaRecords.add(element) if (!cursor.moveToNext()) { break } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Adapter.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Adapter.kt index 33a26e0951..5803925a4d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Adapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Adapter.kt @@ -3,12 +3,14 @@ package org.thoughtcrime.securesms.mediapreview import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.viewpager2.adapter.FragmentStateAdapter +import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.attachments.Attachment import org.thoughtcrime.securesms.mediasend.Media import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.adapter.StableIdGenerator class MediaPreviewV2Adapter(fragment: Fragment) : FragmentStateAdapter(fragment) { + private val TAG = Log.tag(MediaPreviewV2Adapter::class.java) private var items: List = listOf() private val stableIdGenerator = StableIdGenerator() private val currentIdSet: HashSet = HashSet() diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java index 3bc7cab45f..68cd992668 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java @@ -6,6 +6,7 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -42,6 +43,12 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment { View itemView = inflater.inflate(R.layout.media_preview_video_fragment, container, false); Bundle arguments = requireArguments(); Uri uri = arguments.getParcelable(DATA_URI); + if (uri == null) { + Log.w(TAG, "Media URI was null."); + Toast.makeText(requireContext(), R.string.MediaPreviewActivity_media_no_longer_available, Toast.LENGTH_LONG).show(); + requireActivity().finish(); + return itemView; + } String contentType = arguments.getString(DATA_CONTENT_TYPE); long size = arguments.getLong(DATA_SIZE); boolean autoPlay = arguments.getBoolean(AUTO_PLAY); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java index 4b2dcb6152..9d86652922 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java @@ -7,6 +7,7 @@ import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.data.DataFetcher; import org.signal.core.util.logging.Log; +import org.signal.libsignal.protocol.InvalidMacException; import org.signal.libsignal.protocol.InvalidMessageException; import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream; @@ -39,7 +40,7 @@ class AttachmentStreamLocalUriFetcher implements DataFetcher { public void loadData(@NonNull Priority priority, @NonNull DataCallback callback) { try { if (!digest.isPresent()) throw new InvalidMessageException("No attachment digest!"); - is = AttachmentCipherInputStream.createForAttachment(attachment, plaintextLength, key, digest.get(), incrementalDigest.get()); + is = AttachmentCipherInputStream.createForAttachment(attachment, plaintextLength, key, digest.get(), incrementalDigest.orElse(null)); callback.onDataReady(is); } catch (IOException | InvalidMessageException e) { callback.onLoadFailed(e); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index 3bb3be6c30..679e66f0f4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -113,6 +113,7 @@ public final class FeatureFlags { private static final String PROMPT_FOR_NOTIFICATION_CONFIG = "android.logs.promptNotificationsConfig"; public static final String PROMPT_BATTERY_SAVER = "android.promptBatterySaver"; public static final String USERNAMES = "android.usernames"; + public static final String INSTANT_VIDEO_PLAYBACK = "android.instantVideoPlayback"; /** * We will only store remote values for flags in this set. If you want a flag to be controllable @@ -177,7 +178,8 @@ public final class FeatureFlags { PROMPT_FOR_NOTIFICATION_LOGS, PROMPT_FOR_NOTIFICATION_CONFIG, PROMPT_BATTERY_SAVER, - USERNAMES + USERNAMES, + INSTANT_VIDEO_PLAYBACK ); @VisibleForTesting @@ -629,6 +631,14 @@ public final class FeatureFlags { } } + /** + * Allow the video players to read from the temporary download files for attachments. + * @return whether this functionality is enabled. + */ + public static boolean instantVideoPlayback() { + return getBoolean(INSTANT_VIDEO_PLAYBACK, false); + } + public static String promptForDelayedNotificationLogs() { return getString(PROMPT_FOR_NOTIFICATION_LOGS, "*"); } @@ -641,6 +651,7 @@ public final class FeatureFlags { return getString(PROMPT_BATTERY_SAVER, "*"); } + /** Only for rendering debug info. */ public static synchronized @NonNull Map getMemoryValues() { return new TreeMap<>(REMOTE_VALUES); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java index b4a0e0675f..78af074104 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java @@ -446,7 +446,7 @@ public class MediaUtil { { try { AttachmentId attachmentId = PartAuthority.requireAttachmentId(uri); - MediaDataSource source = SignalDatabase.attachments().mediaDataSourceFor(attachmentId); + MediaDataSource source = SignalDatabase.attachments().mediaDataSourceFor(attachmentId, false); return extractFrame(source, timeUs); } catch (IOException e) { Log.w(TAG, "Failed to extract frame for URI: " + uri, e); diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/exo/PartDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/video/exo/PartDataSource.java index f952b8c19d..2bf8a36429 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/exo/PartDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/exo/PartDataSource.java @@ -1,24 +1,29 @@ package org.thoughtcrime.securesms.video.exo; -import android.content.Context; import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import androidx.annotation.OptIn; import androidx.media3.common.util.UnstableApi; import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DataSpec; import androidx.media3.datasource.TransferListener; -import org.thoughtcrime.securesms.attachments.Attachment; +import org.signal.core.util.logging.Log; +import org.signal.libsignal.protocol.InvalidMessageException; +import org.signal.libsignal.protocol.incrementalmac.ChunkSizeChoice; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.database.AttachmentTable; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.mms.PartUriParser; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.FeatureFlags; +import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream; import java.io.EOFException; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Collections; @@ -28,14 +33,13 @@ import java.util.Map; @OptIn(markerClass = UnstableApi.class) class PartDataSource implements DataSource { - private final @NonNull Context context; + private final String TAG = Log.tag(PartDataSource.class); private final @Nullable TransferListener listener; private Uri uri; - private InputStream inputSteam; + private InputStream inputStream; - PartDataSource(@NonNull Context context, @Nullable TransferListener listener) { - this.context = context.getApplicationContext(); + PartDataSource(@Nullable TransferListener listener) { this.listener = listener; } @@ -47,13 +51,42 @@ class PartDataSource implements DataSource { public long open(DataSpec dataSpec) throws IOException { this.uri = dataSpec.uri; - AttachmentTable attachmentDatabase = SignalDatabase.attachments(); - PartUriParser partUri = new PartUriParser(uri); - Attachment attachment = attachmentDatabase.getAttachment(partUri.getPartId()); + AttachmentTable attachmentDatabase = SignalDatabase.attachments(); + PartUriParser partUri = new PartUriParser(uri); + DatabaseAttachment attachment = attachmentDatabase.getAttachment(partUri.getPartId()); if (attachment == null) throw new IOException("Attachment not found"); - this.inputSteam = attachmentDatabase.getAttachmentStream(partUri.getPartId(), dataSpec.position); + final boolean hasIncrementalDigest = attachment.getIncrementalDigest() != null; + final boolean inProgress = attachment.isInProgress(); + final String attachmentKey = attachment.getKey(); + final boolean hasData = attachment.hasData(); + if (inProgress && !hasData && hasIncrementalDigest && attachmentKey != null && FeatureFlags.instantVideoPlayback()) { + final byte[] decode = Base64.decode(attachmentKey); + final File transferFile = attachmentDatabase.getOrCreateTransferFile(attachment.getAttachmentId()); + try { + this.inputStream = AttachmentCipherInputStream.createForAttachment(transferFile, attachment.getSize(), decode, attachment.getDigest(), attachment.getIncrementalDigest()); + + long skipped = 0; + while (skipped < dataSpec.position) { + skipped += this.inputStream.read(); + } + + Log.d(TAG, "Successfully loaded partial attachment file."); + + } catch (InvalidMessageException e) { + throw new IOException("Error decrypting attachment stream!", e); + } + } else if (!inProgress || hasData) { + this.inputStream = attachmentDatabase.getAttachmentStream(partUri.getPartId(), dataSpec.position); + + Log.d(TAG, "Successfully loaded completed attachment file."); + } else { + throw new IOException("Ineligible " + attachment.getAttachmentId().toString() + + "\nTransfer state: " + attachment.getTransferState() + + "\nIncremental Digest Present: " + hasIncrementalDigest + + "\nAttachment Key Non-Empty: " + (attachmentKey != null && !attachmentKey.isEmpty())); + } if (listener != null) { listener.onTransferStart(this, dataSpec, false); @@ -66,7 +99,7 @@ class PartDataSource implements DataSource { @Override public int read(@NonNull byte[] buffer, int offset, int readLength) throws IOException { - int read = inputSteam.read(buffer, offset, readLength); + int read = inputStream.read(buffer, offset, readLength); if (read > 0 && listener != null) { listener.onBytesTransferred(this, null, false, read); @@ -87,6 +120,6 @@ class PartDataSource implements DataSource { @Override public void close() throws IOException { - inputSteam.close(); + if (inputStream != null) inputStream.close(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/exo/SignalDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/video/exo/SignalDataSource.java index 58098f467f..9033247417 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/exo/SignalDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/exo/SignalDataSource.java @@ -116,7 +116,7 @@ import okhttp3.OkHttpClient; @Override public @NonNull SignalDataSource createDataSource() { return new SignalDataSource(new DefaultDataSourceFactory(context, "GenericUserAgent", null).createDataSource(), - new PartDataSource(context, listener), + new PartDataSource(listener), new BlobDataSource(context, listener), okHttpClient != null ? new ChunkedDataSource(okHttpClient, listener) : null); } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java index e0abd3df74..1c560d2de0 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java @@ -159,7 +159,7 @@ public class SignalServiceMessageReceiver { if (!pointer.getDigest().isPresent()) throw new InvalidMessageException("No attachment digest!"); socket.retrieveAttachment(pointer.getCdnNumber(), pointer.getRemoteId(), destination, maxSizeBytes, listener); - return AttachmentCipherInputStream.createForAttachment(destination, pointer.getSize().orElse(0), pointer.getKey(), pointer.getDigest().get(), pointer.getIncrementalDigest().orElse(new byte[0])); + return AttachmentCipherInputStream.createForAttachment(destination, pointer.getSize().orElse(0), pointer.getKey(), pointer.getDigest().get(), null); } public InputStream retrieveSticker(byte[] packId, byte[] packKey, int stickerId) diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java index b9d853708c..602a3548ca 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java @@ -69,22 +69,22 @@ public class AttachmentCipherInputStream extends FilterInputStream { throw new InvalidMacException("Missing digest!"); } - try (FileInputStream fin = new FileInputStream(file)) { - verifyMac(fin, file.length(), mac, digest); - } - - final FileInputStream innerStream = new FileInputStream(file); + final InputStream wrappedStream; boolean hasIncrementalMac = incrementalDigest != null && incrementalDigest.length > 0; - - InputStream wrap = !hasIncrementalMac ? innerStream - : new IncrementalMacInputStream( - innerStream, - parts[1], - ChunkSizeChoice.inferChunkSize(Math.max(Math.toIntExact(file.length()), 1)), - incrementalDigest); - - InputStream inputStream = new AttachmentCipherInputStream(wrap, parts[0], file.length() - BLOCK_SIZE - mac.getMacLength()); + if (!hasIncrementalMac) { + try (FileInputStream macVerificationStream = new FileInputStream(file)) { + verifyMac(macVerificationStream, file.length(), mac, digest); + } + wrappedStream = new FileInputStream(file); + } else { + wrappedStream = new IncrementalMacInputStream( + new FileInputStream(file), + parts[1], + ChunkSizeChoice.inferChunkSize(Math.toIntExact(plaintextLength)), + incrementalDigest); + } + InputStream inputStream = new AttachmentCipherInputStream(wrappedStream, parts[0], file.length() - BLOCK_SIZE - mac.getMacLength()); if (plaintextLength != 0) { inputStream = new ContentLengthInputStream(inputStream, plaintextLength); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/http/DigestingRequestBody.kt b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/http/DigestingRequestBody.kt index 65eb6de8a0..403d919f58 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/http/DigestingRequestBody.kt +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/http/DigestingRequestBody.kt @@ -81,7 +81,7 @@ class DigestingRequestBody( return if (contentLength > 0) contentLength - contentStart else -1 } - fun getAttachmentDigest() = AttachmentDigest(transmittedDigest, incrementalDigest) + fun getAttachmentDigest(): AttachmentDigest = AttachmentDigest(transmittedDigest, incrementalDigest) private fun logMessage(actual: Long, expected: Long): String { val difference = actual - expected diff --git a/libsignal/service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java b/libsignal/service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java index b52d753662..1ce1a999e6 100644 --- a/libsignal/service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java +++ b/libsignal/service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java @@ -67,7 +67,7 @@ public final class AttachmentCipherTest { try { byte[] key = Util.getSecretBytes(64); byte[] plaintextInput = "Gwen Stacy".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key, true); + EncryptResult encryptResult = encryptData(plaintextInput, key, false); byte[] badKey = new byte[64]; cipherFile = writeToFile(encryptResult.ciphertext); @@ -92,7 +92,7 @@ public final class AttachmentCipherTest { try { byte[] key = Util.getSecretBytes(64); byte[] plaintextInput = "Mary Jane Watson".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key, true); + EncryptResult encryptResult = encryptData(plaintextInput, key, false); byte[] badDigest = new byte[32]; cipherFile = writeToFile(encryptResult.ciphertext); @@ -213,7 +213,7 @@ public final class AttachmentCipherTest { try { byte[] key = Util.getSecretBytes(64); byte[] plaintextInput = "Uncle Ben".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key, true); + EncryptResult encryptResult = encryptData(plaintextInput, key, false); byte[] badMacCiphertext = Arrays.copyOf(encryptResult.ciphertext, encryptResult.ciphertext.length); badMacCiphertext[badMacCiphertext.length - 1] += 1; @@ -221,6 +221,7 @@ public final class AttachmentCipherTest { cipherFile = writeToFile(badMacCiphertext); AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, encryptResult.digest, encryptResult.incrementalDigest); + fail(); } catch (InvalidMessageException e) { hitCorrectException = true; } finally {