diff --git a/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarRenderer.kt b/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarRenderer.kt index 5cf4cbb616..246f8309c1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarRenderer.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarRenderer.kt @@ -134,6 +134,6 @@ object AvatarRenderer { } private fun createMedia(uri: Uri, size: Long): Media { - return Media(uri, MediaUtil.IMAGE_JPEG, System.currentTimeMillis(), DIMENSIONS, DIMENSIONS, size, 0, false, false, Optional.empty(), Optional.empty(), Optional.empty()) + return Media(uri, MediaUtil.IMAGE_JPEG, System.currentTimeMillis(), DIMENSIONS, DIMENSIONS, size, 0, false, false, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragmentArgs.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragmentArgs.kt index 7309384c98..0aca847a74 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragmentArgs.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragmentArgs.kt @@ -199,7 +199,8 @@ data class MultiselectForwardFragmentArgs @JvmOverloads constructor( videoGif, Optional.empty(), Optional.ofNullable(caption), - Optional.ofNullable(transformProperties) + Optional.ofNullable(transformProperties), + Optional.ofNullable(fileName) ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index b5ddab7712..5bd9b76317 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -249,6 +249,7 @@ import org.thoughtcrime.securesms.messagedetails.MessageDetailsFragment import org.thoughtcrime.securesms.messagerequests.MessageRequestRepository import org.thoughtcrime.securesms.mms.AttachmentManager import org.thoughtcrime.securesms.mms.AudioSlide +import org.thoughtcrime.securesms.mms.DocumentSlide import org.thoughtcrime.securesms.mms.GifSlide import org.thoughtcrime.securesms.mms.ImageSlide import org.thoughtcrime.securesms.mms.MediaConstraints @@ -1206,7 +1207,7 @@ class ConversationFragment : if (mediaType == SlideFactory.MediaType.VCARD) { conversationActivityResultContracts.launchContactShareEditor(uri, viewModel.recipientSnapshot!!.chatColors) - } else if (mediaType == SlideFactory.MediaType.IMAGE || mediaType == SlideFactory.MediaType.GIF || mediaType == SlideFactory.MediaType.VIDEO) { + } else { val mimeType = MediaUtil.getMimeType(requireContext(), uri) ?: mediaType.toFallbackMimeType() val media = Media( uri, @@ -1220,11 +1221,10 @@ class ConversationFragment : videoGif, Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty() ) conversationActivityResultContracts.launchMediaEditor(listOf(media), recipientId, composeText.textTrimmed) - } else { - attachmentManager.setMedia(Glide.with(this), uri, mediaType, MediaConstraints.getPushMediaConstraints(), width, height) } } @@ -3614,6 +3614,7 @@ class ConversationFragment : MediaUtil.isVideoType(it.mimeType) -> VideoSlide(requireContext(), it.uri, it.size, it.isVideoGif, it.width, it.height, it.caption.orNull(), it.transformProperties.orNull()) MediaUtil.isGif(it.mimeType) -> GifSlide(requireContext(), it.uri, it.size, it.width, it.height, it.isBorderless, it.caption.orNull()) MediaUtil.isImageType(it.mimeType) -> ImageSlide(requireContext(), it.uri, it.mimeType, it.size, it.width, it.height, it.isBorderless, it.caption.orNull(), null, it.transformProperties.orNull()) + MediaUtil.isDocumentType(it.mimeType) -> { DocumentSlide(requireContext(), it.uri, it.mimeType, it.size, it.fileName.orNull()) } else -> { Log.w(TAG, "Asked to send an unexpected mimeType: '${it.mimeType}'. Skipping.") null diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java index 26cd4623d4..283e64be36 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java @@ -126,7 +126,7 @@ public class GiphyActivity extends PassphraseRequiredActivity implements Keyboar mimeType = mediaType.toFallbackMimeType(); } - Media media = new Media(success.getBlobUri(), mimeType, 0, success.getWidth(), success.getHeight(), 0, 0, false, true, Optional.empty(), Optional.empty(), Optional.empty()); + Media media = new Media(success.getBlobUri(), mimeType, 0, success.getWidth(), success.getHeight(), 0, 0, false, true, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); startActivityForResult(MediaSelectionActivity.editor(this, sendType, Collections.singletonList(media), recipientId, text), MEDIA_SENDER); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt index 90e39e84e2..347989efba 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt @@ -131,6 +131,7 @@ fun MediaTable.MediaRecord.toMedia(): Media? { attachment.videoGif, Optional.empty(), Optional.ofNullable(attachment.caption), + Optional.empty(), Optional.empty() ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/AvatarSelectionActivity.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/AvatarSelectionActivity.java index 09b1c610c5..a1b5f2b5ac 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/AvatarSelectionActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/AvatarSelectionActivity.java @@ -101,6 +101,7 @@ public class AvatarSelectionActivity extends AppCompatActivity implements Camera false, Optional.of(Media.ALL_MEDIA_BUCKET_ID), Optional.empty(), + Optional.empty(), Optional.empty())); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/ImageEditorModelRenderMediaTransform.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/ImageEditorModelRenderMediaTransform.java index 9504a755f4..69b8f0047f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/ImageEditorModelRenderMediaTransform.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/ImageEditorModelRenderMediaTransform.java @@ -50,7 +50,7 @@ public final class ImageEditorModelRenderMediaTransform implements MediaTransfor .withMimeType(MediaUtil.IMAGE_JPEG) .createForSingleSessionOnDisk(context); - return new Media(uri, MediaUtil.IMAGE_JPEG, media.getDate(), bitmap.getWidth(), bitmap.getHeight(), outputStream.size(), 0, false, false, media.getBucketId(), media.getCaption(), Optional.empty()); + return new Media(uri, MediaUtil.IMAGE_JPEG, media.getDate(), bitmap.getWidth(), bitmap.getHeight(), outputStream.size(), 0, false, false, media.getBucketId(), media.getCaption(), Optional.empty(), Optional.empty()); } catch (IOException e) { Log.w(TAG, "Failed to render image. Using base image."); return media; diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/Media.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/Media.java index e4899cdc4b..e08a6a07e1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/Media.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/Media.java @@ -31,9 +31,10 @@ public class Media implements Parcelable { private final boolean borderless; private final boolean videoGif; - private Optional bucketId; + private Optional bucketId; private Optional caption; private Optional transformProperties; + private Optional fileName; public Media(@NonNull Uri uri, @NonNull String mimeType, @@ -46,7 +47,8 @@ public class Media implements Parcelable { boolean videoGif, Optional bucketId, Optional caption, - Optional transformProperties) + Optional transformProperties, + Optional fileName) { this.uri = uri; this.mimeType = mimeType; @@ -60,6 +62,7 @@ public class Media implements Parcelable { this.bucketId = bucketId; this.caption = caption; this.transformProperties = transformProperties; + this.fileName = fileName; } protected Media(Parcel in) { @@ -80,6 +83,7 @@ public class Media implements Parcelable { } catch (IOException e) { throw new AssertionError(e); } + fileName = Optional.ofNullable(in.readString()); } public Uri getUri() { @@ -130,6 +134,14 @@ public class Media implements Parcelable { this.caption = Optional.ofNullable(caption); } + public Optional getFileName() { + return fileName; + } + + public void setFileName(String name) { + this.fileName = Optional.ofNullable(name); + } + public Optional getTransformProperties() { return transformProperties; } @@ -153,6 +165,7 @@ public class Media implements Parcelable { dest.writeString(bucketId.orElse(null)); dest.writeString(caption.orElse(null)); dest.writeString(transformProperties.map(JsonUtil::toJson).orElse(null)); + dest.writeString(fileName.orElse(null)); } public static final Creator CREATOR = new Creator() { @@ -194,7 +207,8 @@ public class Media implements Parcelable { media.isVideoGif(), media.getBucketId(), media.getCaption(), - media.getTransformProperties()); + media.getTransformProperties(), + media.getFileName()); } public static @NonNull Media stripTransform(@NonNull Media media) { @@ -211,6 +225,7 @@ public class Media implements Parcelable { media.isVideoGif(), media.getBucketId(), media.getCaption(), - Optional.empty()); + Optional.empty(), + media.getFileName()); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java index 4b774f444b..0a750a8003 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java @@ -275,7 +275,7 @@ public class MediaRepository { long size = cursor.getLong(cursor.getColumnIndexOrThrow(Images.Media.SIZE)); long duration = !isImage ? cursor.getInt(cursor.getColumnIndexOrThrow(Video.Media.DURATION)) : 0; - media.add(fixMimeType(context, new Media(uri, mimetype, date, width, height, size, duration, false, false, Optional.of(bucketId), Optional.empty(), Optional.empty()))); + media.add(fixMimeType(context, new Media(uri, mimetype, date, width, height, size, duration, false, false, Optional.of(bucketId), Optional.empty(), Optional.empty(), Optional.empty()))); } } @@ -366,7 +366,7 @@ public class MediaRepository { height = dimens.second; } - return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, 0, media.isBorderless(), media.isVideoGif(), media.getBucketId(), media.getCaption(), Optional.empty()); + return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, 0, media.isBorderless(), media.isVideoGif(), media.getBucketId(), media.getCaption(), Optional.empty(), Optional.empty()); } private Media getContentResolverPopulatedMedia(@NonNull Context context, @NonNull Media media) throws IOException { @@ -392,7 +392,7 @@ public class MediaRepository { height = dimens.second; } - return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, 0, media.isBorderless(), media.isVideoGif(), media.getBucketId(), media.getCaption(), Optional.empty()); + return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, 0, media.isBorderless(), media.isVideoGif(), media.getBucketId(), media.getCaption(), Optional.empty(), Optional.empty()); } @VisibleForTesting diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendDocumentFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendDocumentFragment.kt new file mode 100644 index 0000000000..53be9cd101 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendDocumentFragment.kt @@ -0,0 +1,139 @@ +package org.thoughtcrime.securesms.mediasend + +import android.database.Cursor +import android.net.Uri +import android.os.Bundle +import android.provider.OpenableColumns +import android.view.View +import android.widget.TextView +import android.widget.Toast +import androidx.fragment.app.Fragment +import org.signal.core.util.getParcelableCompat +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.mms.PartAuthority +import org.thoughtcrime.securesms.util.MediaUtil +import org.thoughtcrime.securesms.util.Util +import java.io.IOException +import java.util.Optional + +/** + * Fragment to show full screen document attachments + */ +class MediaSendDocumentFragment : Fragment(R.layout.mediasend_document_fragment), MediaSendPageFragment { + + companion object { + private val TAG = Log.tag(MediaSendDocumentFragment::class.java) + + private const val KEY_MEDIA = "media" + + fun newInstance(media: Media): MediaSendDocumentFragment { + val args = Bundle() + args.putParcelable(KEY_MEDIA, media) + + val fragment = MediaSendDocumentFragment() + fragment.arguments = args + fragment.uri = media.uri + return fragment + } + } + + private lateinit var uri: Uri + private lateinit var media: Media + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val name: TextView = view.findViewById(R.id.name) + val size: TextView = view.findViewById(R.id.size) + val extension: TextView = view.findViewById(R.id.extension) + + this.media = requireNotNull(requireArguments().getParcelableCompat(KEY_MEDIA, Media::class.java)) + + val fileInfo: Pair? = getFileInfo() + if (fileInfo != null) { + media.setFileName(fileInfo.first) + name.text = fileInfo.first ?: getString(R.string.DocumentView_unnamed_file) + size.text = Util.getPrettyFileSize(fileInfo.second) + + val extensionText: String = MediaUtil.getFileType(requireContext(), Optional.ofNullable(fileInfo.first), media.uri).orElse("") + if (extensionText.length <= 3) { + extension.text = extensionText + extension.setTextAppearance(requireContext(), R.style.Signal_Text_BodySmall) + } else if (extensionText.length == 4) { + extension.text = extensionText + extension.setTextAppearance(requireContext(), R.style.Signal_Text_Caption) + } + } else { + Toast.makeText(requireContext(), R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, Toast.LENGTH_SHORT).show() + requireActivity().finishAfterTransition() + } + } + + override fun getUri(): Uri { + return uri + } + + override fun setUri(uri: Uri) { + this.uri = uri + } + + override fun saveState(): Any = Unit + + override fun restoreState(state: Any) = Unit + + override fun notifyHidden() = Unit + + private fun getFileInfo(): Pair? { + val fileInfo: Pair + try { + if (PartAuthority.isLocalUri(uri)) { + fileInfo = getManuallyCalculatedFileInfo(uri) + } else { + val result = getContentResolverFileInfo(uri) + fileInfo = if ((result == null)) getManuallyCalculatedFileInfo(uri) else result + } + } catch (e: IOException) { + Log.w(TAG, e) + return null + } + + return fileInfo + } + + @Throws(IOException::class) + private fun getManuallyCalculatedFileInfo(uri: Uri): Pair { + var fileName: String? = null + var fileSize: Long? = null + + if (PartAuthority.isLocalUri(uri)) { + fileSize = PartAuthority.getAttachmentSize(requireContext(), uri) + fileName = PartAuthority.getAttachmentFileName(requireContext(), uri) + } + if (fileSize == null) { + fileSize = MediaUtil.getMediaSize(context, uri) + } + + return Pair(fileName, fileSize) + } + + private fun getContentResolverFileInfo(uri: Uri): Pair? { + var cursor: Cursor? = null + + try { + cursor = requireContext().contentResolver.query(uri, null, null, null, null) + + if (cursor != null && cursor.moveToFirst()) { + val fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) + val fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE)) + media.setFileName(fileName) + + return Pair(fileName, fileSize) + } + } finally { + cursor?.close() + } + + return null + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/SentMediaQualityTransform.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/SentMediaQualityTransform.java index 42cd868452..1b49c2e90a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/SentMediaQualityTransform.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/SentMediaQualityTransform.java @@ -37,6 +37,7 @@ public final class SentMediaQualityTransform implements MediaTransform { media.isVideoGif(), media.getBucketId(), media.getCaption(), - Optional.of(AttachmentTable.TransformProperties.forSentMediaQuality(media.getTransformProperties(), sentMediaQuality))); + Optional.of(AttachmentTable.TransformProperties.forSentMediaQuality(media.getTransformProperties(), sentMediaQuality)), + media.getFileName()); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoTrimTransform.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoTrimTransform.kt index 77aa092f74..6365c5e450 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoTrimTransform.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoTrimTransform.kt @@ -22,7 +22,8 @@ class VideoTrimTransform(private val data: VideoTrimData) : MediaTransform { media.isVideoGif, media.bucketId, media.caption, - Optional.of(TransformProperties(false, data.isDurationEdited, data.startTimeUs, data.endTimeUs, SentMediaQuality.STANDARD.code, false)) + Optional.of(TransformProperties(false, data.isDurationEdited, data.startTimeUs, data.endTimeUs, SentMediaQuality.STANDARD.code, false)), + media.fileName ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaBuilder.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaBuilder.kt index e9f2e1b3a7..f8526595b9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaBuilder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaBuilder.kt @@ -18,6 +18,7 @@ object MediaBuilder { videoGif: Boolean = false, bucketId: Optional = Optional.empty(), caption: Optional = Optional.empty(), - transformProperties: Optional = Optional.empty() - ) = Media(uri, mimeType, date, width, height, size, duration, borderless, videoGif, bucketId, caption, transformProperties) + transformProperties: Optional = Optional.empty(), + fileName: Optional = Optional.empty() + ) = Media(uri, mimeType, date, width, height, size, duration, borderless, videoGif, bucketId, caption, transformProperties, fileName) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt index 34a998b100..65e55afbc3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt @@ -151,6 +151,21 @@ class MediaSelectionRepository(context: Context) { scheduleMessages(sendType, contacts.map { it.recipientId }, trimmedBody, updatedMedia, trimmedMentions, trimmedBodyRanges, isViewOnce, scheduledTime) emitter.onComplete() } + } else if (MediaUtil.isDocumentType(selectedMedia.first().mimeType)) { + Log.i(TAG, "Document. Skipping pre-upload.") + emitter.onSuccess( + MediaSendActivityResult( + recipientId = singleRecipient!!.id, + nonUploadedMedia = updatedMedia, + body = trimmedBody, + messageSendType = sendType, + isViewOnce = isViewOnce, + mentions = trimmedMentions, + bodyRanges = trimmedBodyRanges, + storyType = StoryType.NONE, + scheduledTime = scheduledTime + ) + ) } else { val splitMessage = MessageUtil.getSplitMessage(context, trimmedBody, sendType.calculateCharacters(trimmedBody).maxPrimaryMessageSize) val splitBody = splitMessage.body diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt index bd711932f4..f5c519e220 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt @@ -421,7 +421,7 @@ class MediaSelectionViewModel( } val filteredPreUploadMedia = if (destination is MediaSelectionDestination.SingleRecipient || !Stories.isFeatureEnabled()) { - media + media.filter { !MediaUtil.isDocumentType(it.mimeType) } } else { media.filter { Stories.MediaTransform.canPreUploadMedia(it) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaValidator.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaValidator.kt index e7cd3a844d..98c42e971f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaValidator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaValidator.kt @@ -17,7 +17,7 @@ object MediaValidator { var error: FilterError? = null if (!isAllMediaValid) { - error = if (media.all { MediaUtil.isImageOrVideoType(it.mimeType) }) { + error = if (media.all { MediaUtil.isImageOrVideoType(it.mimeType) || MediaUtil.isDocumentType(it.mimeType) }) { FilterError.ItemTooLarge } else { FilterError.ItemInvalidType @@ -53,7 +53,7 @@ object MediaValidator { return media .filter { m -> isSupportedMediaType(m.mimeType) } .filter { m -> - MediaUtil.isImageAndNotGif(m.mimeType) || isValidGif(context, m, mediaConstraints) || isValidVideo(context, m, mediaConstraints) + MediaUtil.isImageAndNotGif(m.mimeType) || isValidGif(context, m, mediaConstraints) || isValidVideo(context, m, mediaConstraints) || isValidDocument(context, m, mediaConstraints) } .filter { m -> !isStory || Stories.MediaTransform.getSendRequirements(m) != Stories.MediaTransform.SendRequirements.CAN_NOT_SEND @@ -68,8 +68,12 @@ object MediaValidator { return MediaUtil.isVideoType(media.mimeType) && media.size < mediaConstraints.getUncompressedVideoMaxSize(context) } + private fun isValidDocument(context: Context, media: Media, mediaConstraints: MediaConstraints): Boolean { + return MediaUtil.isDocumentType(media.mimeType) && media.size < mediaConstraints.getDocumentMaxSize(context) + } + private fun isSupportedMediaType(mimeType: String): Boolean { - return MediaUtil.isGif(mimeType) || MediaUtil.isImageType(mimeType) || MediaUtil.isVideoType(mimeType) + return MediaUtil.isGif(mimeType) || MediaUtil.isImageType(mimeType) || MediaUtil.isVideoType(mimeType) || MediaUtil.isDocumentType(mimeType) } data class FilterResult(val filteredMedia: List, val filterError: FilterError?, val bucketId: String?) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/capture/MediaCaptureRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/capture/MediaCaptureRepository.kt index 0288ff9d66..2ff22ea411 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/capture/MediaCaptureRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/capture/MediaCaptureRepository.kt @@ -106,6 +106,7 @@ class MediaCaptureRepository(context: Context) { false, Optional.of(Media.ALL_MEDIA_BUCKET_ID), Optional.empty(), + Optional.empty(), Optional.empty() ) } catch (e: IOException) { @@ -160,6 +161,7 @@ class MediaCaptureRepository(context: Context) { false, Optional.of(bucketId), Optional.empty(), + Optional.empty(), Optional.empty() ) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/documents/MediaReviewDocumentPageFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/documents/MediaReviewDocumentPageFragment.kt new file mode 100644 index 0000000000..042d001050 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/documents/MediaReviewDocumentPageFragment.kt @@ -0,0 +1,63 @@ +package org.thoughtcrime.securesms.mediasend.v2.documents + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import org.signal.core.util.getParcelableCompat +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.mediasend.Media +import org.thoughtcrime.securesms.mediasend.MediaSendDocumentFragment +import org.thoughtcrime.securesms.mediasend.v2.HudCommand +import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionViewModel + +private const val DOCUMENT_TAG = "media.send.document.fragment" + +/** + * Fragment which ensures we fire off ResumeEntryTransition when viewing a document. + */ +class MediaReviewDocumentPageFragment : Fragment(R.layout.fragment_container) { + + private lateinit var mediaSendDocumentFragment: MediaSendDocumentFragment + + private val sharedViewModel: MediaSelectionViewModel by viewModels(ownerProducer = { requireActivity() }) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + mediaSendDocumentFragment = ensureFragment() + sharedViewModel.sendCommand(HudCommand.ResumeEntryTransition) + } + + private fun ensureFragment(): MediaSendDocumentFragment { + val fragmentInManager: MediaSendDocumentFragment? = childFragmentManager.findFragmentByTag(DOCUMENT_TAG) as? MediaSendDocumentFragment + + return if (fragmentInManager != null) { + fragmentInManager + } else { + val mediaSendDocumentFragment = MediaSendDocumentFragment.newInstance(requireMedia()) + + childFragmentManager.beginTransaction() + .replace( + R.id.fragment_container, + mediaSendDocumentFragment, + DOCUMENT_TAG + ) + .commitAllowingStateLoss() + + mediaSendDocumentFragment + } + } + + private fun requireMedia(): Media = requireNotNull(requireArguments().getParcelableCompat(ARG_MEDIA, Media::class.java)) + + companion object { + private const val ARG_MEDIA = "arg.media" + + fun newInstance(media: Media): Fragment { + return MediaReviewDocumentPageFragment().apply { + arguments = Bundle().apply { + putParcelable(ARG_MEDIA, media) + } + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/AddMessageDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/AddMessageDialogFragment.kt index cafde95238..3a239237a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/AddMessageDialogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/AddMessageDialogFragment.kt @@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionViewModel import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.stories.Stories +import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.ViewUtil import org.thoughtcrime.securesms.util.views.Stub import org.thoughtcrime.securesms.util.visible @@ -133,7 +134,7 @@ class AddMessageDialogFragment : KeyboardEntryDialogFragment(R.layout.v2_media_a binding.content.addAMessageInput.text = null dismiss() } - binding.content.viewOnceToggle.visible = state.selectedMedia.size == 1 && !state.isStory + binding.content.viewOnceToggle.visible = state.selectedMedia.size == 1 && !state.isStory && !MediaUtil.isDocumentType(state.focusedMedia?.mimeType) } initializeMentions() diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/MediaReviewFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/MediaReviewFragment.kt index 26b941ac1f..ca54cd7a47 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/MediaReviewFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/MediaReviewFragment.kt @@ -655,7 +655,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul } private fun computeViewOnceButtonAnimators(state: MediaSelectionState): List { - return if (state.isTouchEnabled && state.selectedMedia.size == 1 && !state.isStory) { + return if (state.isTouchEnabled && state.selectedMedia.size == 1 && !state.isStory && !MediaUtil.isDocumentType(state.focusedMedia?.mimeType)) { listOf(MediaReviewAnimatorController.getFadeInAnimator(viewOnceButton)) } else { listOf(MediaReviewAnimatorController.getFadeOutAnimator(viewOnceButton)) @@ -672,7 +672,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul private fun computeAddMediaButtonsAnimators(state: MediaSelectionState): List { return when { - !state.isTouchEnabled || state.viewOnceToggleState == MediaSelectionState.ViewOnceToggleState.ONCE -> { + !state.isTouchEnabled || state.viewOnceToggleState == MediaSelectionState.ViewOnceToggleState.ONCE || MediaUtil.isDocumentType(state.focusedMedia?.mimeType) -> { listOf( MediaReviewAnimatorController.getFadeOutAnimator(addMediaButton), MediaReviewAnimatorController.getFadeOutAnimator(selectionRecycler) @@ -706,7 +706,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul } private fun computeSaveButtonAnimators(state: MediaSelectionState): List { - return if (state.isTouchEnabled && !MediaUtil.isVideo(state.focusedMedia?.mimeType)) { + return if (state.isTouchEnabled && !MediaUtil.isVideo(state.focusedMedia?.mimeType) && !MediaUtil.isDocumentType(state.focusedMedia?.mimeType)) { listOf( MediaReviewAnimatorController.getFadeInAnimator(saveButton) ) @@ -718,7 +718,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul } private fun computeQualityButtonAnimators(state: MediaSelectionState): List { - return if (state.isTouchEnabled && !state.isStory) { + return if (state.isTouchEnabled && !state.isStory && !MediaUtil.isDocumentType(state.focusedMedia?.mimeType)) { listOf(MediaReviewAnimatorController.getFadeInAnimator(qualityButton)) } else { listOf(MediaReviewAnimatorController.getFadeOutAnimator(qualityButton)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/MediaReviewFragmentPagerAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/MediaReviewFragmentPagerAdapter.kt index 1ae6e4db03..55b72a8fdf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/MediaReviewFragmentPagerAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/MediaReviewFragmentPagerAdapter.kt @@ -5,6 +5,7 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.adapter.FragmentStateAdapter import org.thoughtcrime.securesms.mediasend.Media +import org.thoughtcrime.securesms.mediasend.v2.documents.MediaReviewDocumentPageFragment import org.thoughtcrime.securesms.mediasend.v2.gif.MediaReviewGifPageFragment import org.thoughtcrime.securesms.mediasend.v2.images.MediaReviewImagePageFragment import org.thoughtcrime.securesms.mediasend.v2.videos.MediaReviewVideoPageFragment @@ -46,6 +47,7 @@ class MediaReviewFragmentPagerAdapter(fragment: Fragment) : FragmentStateAdapter MediaUtil.isGif(mediaItem.mimeType) -> MediaReviewGifPageFragment.newInstance(mediaItem.uri) MediaUtil.isImageType(mediaItem.mimeType) -> MediaReviewImagePageFragment.newInstance(mediaItem.uri) MediaUtil.isVideoType(mediaItem.mimeType) -> MediaReviewVideoPageFragment.newInstance(mediaItem.uri, mediaItem.isVideoGif) + MediaUtil.isDocumentType(mediaItem.mimeType) -> MediaReviewDocumentPageFragment.newInstance(mediaItem) else -> { throw UnsupportedOperationException("Can only render images and videos. Found mimetype: '" + mediaItem.mimeType + "'") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java index 9f25a81920..0d89d957b3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -17,25 +17,19 @@ package org.thoughtcrime.securesms.mms; import android.Manifest; -import android.annotation.SuppressLint; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; -import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.PorterDuff; import android.net.Uri; -import android.os.AsyncTask; import android.provider.ContactsContract; -import android.provider.OpenableColumns; -import android.util.Pair; import android.view.View; import android.widget.Toast; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; @@ -50,7 +44,6 @@ import org.signal.core.util.concurrent.SettableFuture; import org.signal.core.util.concurrent.SimpleTask; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.components.AudioView; import org.thoughtcrime.securesms.components.DocumentView; import org.thoughtcrime.securesms.components.RemovableEditableMediaView; @@ -58,7 +51,6 @@ import org.thoughtcrime.securesms.components.ThumbnailView; import org.thoughtcrime.securesms.components.location.SignalMapView; import org.thoughtcrime.securesms.components.location.SignalPlace; import org.thoughtcrime.securesms.conversation.MessageSendType; -import org.thoughtcrime.securesms.database.AttachmentTable; import org.thoughtcrime.securesms.database.MediaTable; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.giph.ui.GiphyActivity; @@ -256,149 +248,6 @@ public class AttachmentManager { attachmentListener.onAttachmentChanged(); } - @SuppressLint("StaticFieldLeak") - public ListenableFuture setMedia(@NonNull final RequestManager requestManager, - @NonNull final Uri uri, - @NonNull final SlideFactory.MediaType mediaType, - @NonNull final MediaConstraints constraints, - final int width, - final int height) - { - inflateStub(); - - final SettableFuture result = new SettableFuture<>(); - - new AsyncTask() { - private boolean areConstraintsSatisfied = false; - - @Override - protected void onPreExecute() { - thumbnail.clear(requestManager); - thumbnail.showProgressSpinner(); - attachmentViewStub.get().setVisibility(View.VISIBLE); - } - - @Override - protected @Nullable Slide doInBackground(Void... params) { - Slide slide; - try { - if (PartAuthority.isLocalUri(uri)) { - slide = getManuallyCalculatedSlideInfo(uri, width, height); - } else { - Slide result = getContentResolverSlideInfo(uri, width, height); - slide = (result == null) ? getManuallyCalculatedSlideInfo(uri, width, height) : result; - } - } catch (IOException e) { - Log.w(TAG, e); - return null; - } - - this.areConstraintsSatisfied = areConstraintsSatisfied(context, slide, constraints); - return slide; - } - - @Override - protected void onPostExecute(@Nullable final Slide slide) { - if (slide == null) { - attachmentViewStub.get().setVisibility(View.GONE); - Toast.makeText(context, - R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, - Toast.LENGTH_SHORT).show(); - result.set(false); - } else if (!areConstraintsSatisfied) { - attachmentViewStub.get().setVisibility(View.GONE); - Toast.makeText(context, - R.string.ConversationActivity_attachment_exceeds_size_limits, - Toast.LENGTH_SHORT).show(); - result.set(false); - } else { - setSlide(slide); - attachmentViewStub.get().setVisibility(View.VISIBLE); - - if (slide.hasAudio()) { - audioView.setAudio((AudioSlide) slide, null, false, false); - removableMediaView.display(audioView, false); - result.set(true); - } else if (slide.hasDocument()) { - documentView.setDocument((DocumentSlide) slide, false); - removableMediaView.display(documentView, false); - result.set(true); - } else { - Attachment attachment = slide.asAttachment(); - result.deferTo(thumbnail.setImageResource(requestManager, slide, false, true, attachment.width, attachment.height)); - removableMediaView.display(thumbnail, mediaType == SlideFactory.MediaType.IMAGE); - } - - attachmentListener.onAttachmentChanged(); - } - } - - private @Nullable Slide getContentResolverSlideInfo(Uri uri, int width, int height) { - Cursor cursor = null; - long start = System.currentTimeMillis(); - - try { - cursor = context.getContentResolver().query(uri, null, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - String fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)); - long fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE)); - String mimeType = context.getContentResolver().getType(uri); - - if (width == 0 || height == 0) { - Pair dimens = MediaUtil.getDimensions(context, mimeType, uri); - width = dimens.first; - height = dimens.second; - } - - Log.d(TAG, "remote slide with size " + fileSize + " took " + (System.currentTimeMillis() - start) + "ms"); - return mediaType.createSlide(context, uri, fileName, mimeType, null, fileSize, width, height, false, null); - } - } finally { - if (cursor != null) cursor.close(); - } - - return null; - } - - private @NonNull Slide getManuallyCalculatedSlideInfo(Uri uri, int width, int height) throws IOException { - long start = System.currentTimeMillis(); - Long mediaSize = null; - String fileName = null; - String mimeType = null; - boolean gif = false; - AttachmentTable.TransformProperties transformProperties = null; - - if (PartAuthority.isLocalUri(uri)) { - mediaSize = PartAuthority.getAttachmentSize(context, uri); - fileName = PartAuthority.getAttachmentFileName(context, uri); - mimeType = PartAuthority.getAttachmentContentType(context, uri); - gif = PartAuthority.getAttachmentIsVideoGif(context, uri); - transformProperties = PartAuthority.getAttachmentTransformProperties(uri); - } - - if (mediaSize == null) { - mediaSize = MediaUtil.getMediaSize(context, uri); - } - - if (mimeType == null) { - mimeType = MediaUtil.getMimeType(context, uri); - } - - if (width == 0 || height == 0) { - Pair dimens = MediaUtil.getDimensions(context, mimeType, uri); - width = dimens.first; - height = dimens.second; - } - - Log.d(TAG, "local slide with size " + mediaSize + " took " + (System.currentTimeMillis() - start) + "ms"); - return mediaType.createSlide(context, uri, fileName, mimeType, null, mediaSize, width, height, gif, transformProperties); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - - return result; - } - public boolean isAttachmentPresent() { return attachmentViewStub.resolved() && attachmentViewStub.get().getVisibility() == View.VISIBLE; } @@ -531,16 +380,6 @@ public class AttachmentManager { } } - @WorkerThread - private boolean areConstraintsSatisfied(final @NonNull Context context, - final @Nullable Slide slide, - final @NonNull MediaConstraints constraints) - { - return slide == null || - constraints.isSatisfied(context, slide.asAttachment()) || - constraints.canResize(slide.asAttachment()); - } - private void previewImageDraft(final @NonNull Slide slide) { if (MediaPreviewV2Fragment.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) { MediaIntentFactory.MediaPreviewArgs args = new MediaIntentFactory.MediaPreviewArgs( diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java index 0892946baa..50453eede9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java @@ -230,34 +230,7 @@ public abstract class Slide { } public @NonNull Optional getFileType(@NonNull Context context) { - Optional fileName = getFileName(); - - if (fileName.isPresent()) { - String fileType = getFileType(fileName); - if (!fileType.isEmpty()) { - return Optional.of(fileType); - } - } - - return Optional.ofNullable(MediaUtil.getExtension(context, getUri())); - } - - private static @NonNull String getFileType(Optional fileName) { - if (!fileName.isPresent()) return ""; - - String[] parts = fileName.get().split("\\."); - - if (parts.length < 2) { - return ""; - } - - String suffix = parts[parts.length - 1]; - - if (suffix.length() <= 3) { - return suffix; - } - - return ""; + return MediaUtil.getFileType(context, getFileName(), getUri()); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareActivity.kt index d37e2d2206..3fb03338d3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareActivity.kt @@ -286,6 +286,7 @@ class ShareActivity : PassphraseRequiredActivity(), MultiselectForwardFragment.C false, Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty() ) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareRepository.kt index 13f953fa3a..860264826c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareRepository.kt @@ -116,6 +116,7 @@ class ShareRepository(context: Context) { false, Optional.of(Media.ALL_MEDIA_BUCKET_ID), Optional.empty(), + Optional.empty(), Optional.empty() ) }.filterNotNull() diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt index 45439f52c6..33c40ffaa5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt @@ -359,7 +359,8 @@ object Stories { media.isVideoGif, media.bucketId, media.caption, - Optional.of(transformProperties) + Optional.of(transformProperties), + media.fileName ) } @@ -398,6 +399,7 @@ object Stories { videoSlide.isVideoGif, Optional.empty(), videoSlide.caption, + Optional.empty(), Optional.empty() ) } 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 763e2e84c9..858ee2fc40 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java @@ -51,6 +51,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URLConnection; +import java.util.Optional; import java.util.concurrent.ExecutionException; public class MediaUtil { @@ -137,6 +138,35 @@ public class MediaUtil { return getCorrectedMimeType(type); } + public static @NonNull Optional getFileType(@NonNull Context context, Optional fileName, Uri uri) { + if (fileName.isPresent()) { + String fileType = getFileType(fileName); + if (!fileType.isEmpty()) { + return Optional.of(fileType); + } + } + + return Optional.ofNullable(MediaUtil.getExtension(context, uri)); + } + + private static @NonNull String getFileType(Optional fileName) { + if (!fileName.isPresent()) return ""; + + String[] parts = fileName.get().split("\\."); + + if (parts.length < 2) { + return ""; + } + + String suffix = parts[parts.length - 1]; + + if (suffix.length() <= 3) { + return suffix; + } + + return ""; + } + public static @Nullable String getExtension(@NonNull Context context, @Nullable Uri uri) { return MimeTypeMap.getSingleton() .getExtensionFromMimeType(getMimeType(context, uri)); @@ -384,6 +414,10 @@ public class MediaUtil { return OCTET.equals(contentType); } + public static boolean isDocumentType(String contentType) { + return !isImageOrVideoType(contentType) && !isGif(contentType) && !isLongTextType(contentType) && !isViewOnceType(contentType); + } + public static boolean hasVideoThumbnail(@NonNull Context context, @Nullable Uri uri) { if (uri == null) { return false; diff --git a/app/src/main/res/layout/mediasend_document_fragment.xml b/app/src/main/res/layout/mediasend_document_fragment.xml new file mode 100644 index 0000000000..8948323bfd --- /dev/null +++ b/app/src/main/res/layout/mediasend_document_fragment.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/test/java/org/thoughtcrime/securesms/mediasend/MediaRepositoryTest.kt b/app/src/test/java/org/thoughtcrime/securesms/mediasend/MediaRepositoryTest.kt index f6d7dd3a7f..7ed7f548f5 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/mediasend/MediaRepositoryTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/mediasend/MediaRepositoryTest.kt @@ -116,7 +116,8 @@ class MediaRepositoryTest { videoGif: Boolean = false, bucketId: Optional = Optional.empty(), caption: Optional = Optional.empty(), - transformProperties: Optional = Optional.empty() + transformProperties: Optional = Optional.empty(), + fileName: Optional = Optional.empty() ): Media { return Media( uri, @@ -130,7 +131,8 @@ class MediaRepositoryTest { videoGif, bucketId, caption, - transformProperties + transformProperties, + fileName ) } }