diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index ec1f49e4f8..00364d4822 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -100,9 +100,9 @@ interface MatrixRoom : Closeable { suspend fun redactEvent(eventId: EventId, reason: String? = null): Result - suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo, progressCallback: ProgressCallback?): Result + suspend fun sendImage(file: File, thumbnailFile: File?, imageInfo: ImageInfo, progressCallback: ProgressCallback?): Result - suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result + suspend fun sendVideo(file: File, thumbnailFile: File?, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 079f144130..fc7215f583 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -360,15 +360,25 @@ class RustMatrixRoom( } } - override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo, progressCallback: ProgressCallback?): Result { - return sendAttachment(listOf(file, thumbnailFile)) { - innerTimeline.sendImage(file.path, thumbnailFile.path, imageInfo.map(), progressCallback?.toProgressWatcher()) + override suspend fun sendImage( + file: File, + thumbnailFile: File?, + imageInfo: ImageInfo, + progressCallback: ProgressCallback?, + ): Result { + return sendAttachment(listOfNotNull(file, thumbnailFile)) { + innerTimeline.sendImage(file.path, thumbnailFile?.path, imageInfo.map(), progressCallback?.toProgressWatcher()) } } - override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result { - return sendAttachment(listOf(file, thumbnailFile)) { - innerTimeline.sendVideo(file.path, thumbnailFile.path, videoInfo.map(), progressCallback?.toProgressWatcher()) + override suspend fun sendVideo( + file: File, + thumbnailFile: File?, + videoInfo: VideoInfo, + progressCallback: ProgressCallback?, + ): Result { + return sendAttachment(listOfNotNull(file, thumbnailFile)) { + innerTimeline.sendVideo(file.path, thumbnailFile?.path, videoInfo.map(), progressCallback?.toProgressWatcher()) } } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index db263fee94..9f036a6b62 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -299,14 +299,14 @@ class FakeMatrixRoom( override suspend fun sendImage( file: File, - thumbnailFile: File, + thumbnailFile: File?, imageInfo: ImageInfo, progressCallback: ProgressCallback? ): Result = fakeSendMedia(progressCallback) override suspend fun sendVideo( file: File, - thumbnailFile: File, + thumbnailFile: File?, videoInfo: VideoInfo, progressCallback: ProgressCallback? ): Result = fakeSendMedia( diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt index dd51198e59..dfcef7fb33 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt @@ -101,7 +101,6 @@ class MediaSender @Inject constructor( progressCallback = progressCallback ) } - is MediaUploadInfo.Video -> { sendVideo( file = uploadInfo.file, diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt index 39c978f625..76e6fd7cb1 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt @@ -26,8 +26,8 @@ sealed interface MediaUploadInfo { val file: File - data class Image(override val file: File, val imageInfo: ImageInfo, val thumbnailFile: File) : MediaUploadInfo - data class Video(override val file: File, val videoInfo: VideoInfo, val thumbnailFile: File) : MediaUploadInfo + data class Image(override val file: File, val imageInfo: ImageInfo, val thumbnailFile: File?) : MediaUploadInfo + data class Video(override val file: File, val videoInfo: VideoInfo, val thumbnailFile: File?) : MediaUploadInfo data class Audio(override val file: File, val audioInfo: AudioInfo) : MediaUploadInfo data class VoiceMessage(override val file: File, val audioInfo: AudioInfo, val waveform: List) : MediaUploadInfo data class AnyFile(override val file: File, val fileInfo: FileInfo) : MediaUploadInfo diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt index f05fe5bdc3..62a3457f90 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt @@ -137,7 +137,7 @@ class AndroidMediaPreProcessor @Inject constructor( resizeMode = ResizeMode.Approximate(IMAGE_SCALE_REF_SIZE, IMAGE_SCALE_REF_SIZE), orientation = orientation, ).getOrThrow() - val thumbnailResult: ThumbnailResult = thumbnailFactory.createImageThumbnail(compressionResult.file) + val thumbnailResult = thumbnailFactory.createImageThumbnail(compressionResult.file) val imageInfo = compressionResult.toImageInfo( mimeType = mimeType, thumbnailResult = thumbnailResult @@ -146,13 +146,13 @@ class AndroidMediaPreProcessor @Inject constructor( return MediaUploadInfo.Image( file = compressionResult.file, imageInfo = imageInfo, - thumbnailFile = thumbnailResult.file + thumbnailFile = thumbnailResult?.file ) } suspend fun processImageWithoutCompression(): MediaUploadInfo { val file = copyToTmpFile(uri) - val thumbnailResult: ThumbnailResult = thumbnailFactory.createImageThumbnail(file) + val thumbnailResult = thumbnailFactory.createImageThumbnail(file) val imageInfo = contentResolver.openInputStream(uri).use { input -> val bitmap = BitmapFactory.decodeStream(input, null, null)!! ImageInfo( @@ -160,16 +160,16 @@ class AndroidMediaPreProcessor @Inject constructor( height = bitmap.height.toLong(), mimetype = mimeType, size = file.length(), - thumbnailInfo = thumbnailResult.info, + thumbnailInfo = thumbnailResult?.info, thumbnailSource = null, - blurhash = thumbnailResult.blurhash, + blurhash = thumbnailResult?.blurhash, ) } removeSensitiveImageMetadata(file) return MediaUploadInfo.Image( file = file, imageInfo = imageInfo, - thumbnailFile = thumbnailResult.file + thumbnailFile = thumbnailResult?.file ) } @@ -197,7 +197,7 @@ class AndroidMediaPreProcessor @Inject constructor( return MediaUploadInfo.Video( file = resultFile, videoInfo = videoInfo, - thumbnailFile = thumbnailInfo.file + thumbnailFile = thumbnailInfo?.file ) } @@ -235,7 +235,7 @@ class AndroidMediaPreProcessor @Inject constructor( } } - private fun extractVideoMetadata(file: File, mimeType: String?, thumbnailResult: ThumbnailResult): VideoInfo = + private fun extractVideoMetadata(file: File, mimeType: String?, thumbnailResult: ThumbnailResult?): VideoInfo = MediaMetadataRetriever().runAndRelease { setDataSource(context, Uri.fromFile(file)) VideoInfo( @@ -244,10 +244,10 @@ class AndroidMediaPreProcessor @Inject constructor( height = extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toLong() ?: 0L, mimetype = mimeType, size = file.length(), - thumbnailInfo = thumbnailResult.info, + thumbnailInfo = thumbnailResult?.info, // Will be computed by the rust sdk thumbnailSource = null, - blurhash = thumbnailResult.blurhash, + blurhash = thumbnailResult?.blurhash, ) } @@ -257,15 +257,15 @@ class AndroidMediaPreProcessor @Inject constructor( } } -fun ImageCompressionResult.toImageInfo(mimeType: String, thumbnailResult: ThumbnailResult) = ImageInfo( +private fun ImageCompressionResult.toImageInfo(mimeType: String, thumbnailResult: ThumbnailResult?) = ImageInfo( width = width.toLong(), height = height.toLong(), mimetype = mimeType, size = size, - thumbnailInfo = thumbnailResult.info, + thumbnailInfo = thumbnailResult?.info, // Will be computed by the rust sdk thumbnailSource = null, - blurhash = thumbnailResult.blurhash, + blurhash = thumbnailResult?.blurhash, ) private fun MediaMetadataRetriever.extractDuration(): Duration { diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt index 40a75b6428..acbee18ec0 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt @@ -61,7 +61,7 @@ class ThumbnailFactory @Inject constructor( ) { @SuppressLint("NewApi") - suspend fun createImageThumbnail(file: File): ThumbnailResult { + suspend fun createImageThumbnail(file: File): ThumbnailResult? { return createThumbnail { cancellationSignal -> // This API works correctly with GIF if (sdkIntProvider.isAtLeast(Build.VERSION_CODES.Q)) { @@ -80,7 +80,7 @@ class ThumbnailFactory @Inject constructor( } } - suspend fun createVideoThumbnail(file: File): ThumbnailResult { + suspend fun createVideoThumbnail(file: File): ThumbnailResult? { return createThumbnail { MediaMetadataRetriever().runAndRelease { setDataSource(context, file.toUri()) @@ -89,32 +89,33 @@ class ThumbnailFactory @Inject constructor( } } - private suspend fun createThumbnail(bitmapFactory: (CancellationSignal) -> Bitmap?): ThumbnailResult = suspendCancellableCoroutine { continuation -> + private suspend fun createThumbnail(bitmapFactory: (CancellationSignal) -> Bitmap?): ThumbnailResult? = suspendCancellableCoroutine { continuation -> val cancellationSignal = CancellationSignal() continuation.invokeOnCancellation { cancellationSignal.cancel() } val bitmapThumbnail: Bitmap? = bitmapFactory(cancellationSignal) + if (bitmapThumbnail == null) { + continuation.resume(null) + return@suspendCancellableCoroutine + } val thumbnailFile = context.createTmpFile(extension = "jpeg") thumbnailFile.outputStream().use { outputStream -> - bitmapThumbnail?.compress(Bitmap.CompressFormat.JPEG, 80, outputStream) - } - val blurhash = bitmapThumbnail?.let { - BlurHash.encode(it, 3, 3) + bitmapThumbnail.compress(Bitmap.CompressFormat.JPEG, 80, outputStream) } + val blurhash = BlurHash.encode(bitmapThumbnail, 3, 3) val thumbnailResult = ThumbnailResult( file = thumbnailFile, info = ThumbnailInfo( - height = bitmapThumbnail?.height?.toLong(), - width = bitmapThumbnail?.width?.toLong(), + height = bitmapThumbnail.height.toLong(), + width = bitmapThumbnail.width.toLong(), mimetype = MimeTypes.Jpeg, size = thumbnailFile.length() ), blurhash = blurhash ) - bitmapThumbnail?.recycle() + bitmapThumbnail.recycle() continuation.resume(thumbnailResult) - } }