diff --git a/CHANGES.md b/CHANGES.md index 28fa27932f..f90a322b09 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,11 +5,12 @@ Features ✨: - Improvements 🙌: - - + - Share image and other media from e2e rooms (#677) Bugfix 🐛: - Fix crash on attachment preview screen (#1088) - "Share" option is not appearing in encrypted rooms for images (#1031) + - Set "image/jpeg" as MIME type of images instead of "image/jpg" (#1075) Translations 🗣: - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt index 48dff4e56d..f78cb7e882 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt @@ -41,4 +41,6 @@ data class ContentAttachmentData( AUDIO, VIDEO } + + fun getSafeMimeType() = if (mimeType == "image/jpg") "image/jpeg" else mimeType } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageAudioContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageAudioContent.kt index e9c6c71882..248e782a74 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageAudioContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageAudioContent.kt @@ -51,4 +51,4 @@ data class MessageAudioContent( * Required if the file is encrypted. Information on the encrypted file, as specified in End-to-end encryption. */ @Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null -) : MessageEncryptedContent +) : MessageWithAttachmentContent diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageFileContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageFileContent.kt index 7c635a401d..f770a2ccea 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageFileContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageFileContent.kt @@ -57,7 +57,7 @@ data class MessageFileContent( * Required if the file is encrypted. Information on the encrypted file, as specified in End-to-end encryption. */ @Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null -) : MessageEncryptedContent { +) : MessageWithAttachmentContent { fun getMimeType(): String { // Mimetype default to plain text, should not be used diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageImageInfoContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageImageInfoContent.kt index 9087a45b4c..be0b5c4bb7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageImageInfoContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageImageInfoContent.kt @@ -20,6 +20,6 @@ package im.vector.matrix.android.api.session.room.model.message /** * A content with image information */ -interface MessageImageInfoContent : MessageEncryptedContent { +interface MessageImageInfoContent : MessageWithAttachmentContent { val info: ImageInfo? } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageVideoContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageVideoContent.kt index 4cf03a5ffd..88d2d72d15 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageVideoContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageVideoContent.kt @@ -51,4 +51,4 @@ data class MessageVideoContent( * Required if the file is encrypted. Information on the encrypted file, as specified in End-to-end encryption. */ @Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null -) : MessageEncryptedContent +) : MessageWithAttachmentContent diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageEncryptedContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageWithAttachmentContent.kt similarity index 89% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageEncryptedContent.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageWithAttachmentContent.kt index 1d1d01c09c..9caf38013f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageEncryptedContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageWithAttachmentContent.kt @@ -21,7 +21,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo /** * Interface for message which can contains an encrypted file */ -interface MessageEncryptedContent : MessageContent { +interface MessageWithAttachmentContent : MessageContent { /** * Required if the file is unencrypted. The URL (typically MXC URI) to the image. */ @@ -36,4 +36,4 @@ interface MessageEncryptedContent : MessageContent { /** * Get the url of the encrypted file or of the file */ -fun MessageEncryptedContent.getFileUrl() = encryptedFileInfo?.url ?: url +fun MessageWithAttachmentContent.getFileUrl() = encryptedFileInfo?.url ?: url diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/FileQualifiers.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/FileQualifiers.kt index dc36b02809..acd6703c77 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/FileQualifiers.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/FileQualifiers.kt @@ -25,3 +25,7 @@ annotation class SessionFilesDirectory @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class SessionCacheDirectory + +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class CacheDirectory diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt index c5b07ff4e8..e929016d4f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt @@ -32,6 +32,7 @@ import im.vector.matrix.android.internal.util.BackgroundDetectionObserver import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import okhttp3.OkHttpClient import org.matrix.olm.OlmManager +import java.io.File @Component(modules = [MatrixModule::class, NetworkModule::class, AuthModule::class]) @MatrixScope @@ -52,6 +53,9 @@ internal interface MatrixComponent { fun resources(): Resources + @CacheDirectory + fun cacheDir(): File + fun olmManager(): OlmManager fun taskExecutor(): TaskExecutor diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt index 284cbfff88..0af22dd65a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.android.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher import org.matrix.olm.OlmManager +import java.io.File import java.util.concurrent.Executors @Module @@ -49,6 +50,13 @@ internal object MatrixModule { return context.resources } + @JvmStatic + @Provides + @CacheDirectory + fun providesCacheDir(context: Context): File { + return context.cacheDir + } + @JvmStatic @Provides @MatrixScope diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultFileService.kt index a5af4a4a7b..37744209a9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultFileService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultFileService.kt @@ -25,6 +25,7 @@ import im.vector.matrix.android.api.session.file.FileService import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments +import im.vector.matrix.android.internal.di.CacheDirectory import im.vector.matrix.android.internal.di.SessionCacheDirectory import im.vector.matrix.android.internal.di.Unauthenticated import im.vector.matrix.android.internal.extensions.foldToCallback @@ -43,8 +44,10 @@ import javax.inject.Inject internal class DefaultFileService @Inject constructor( private val context: Context, - @SessionCacheDirectory + @CacheDirectory private val cacheDirectory: File, + @SessionCacheDirectory + private val sessionCacheDirectory: File, private val contentUrlResolver: ContentUrlResolver, @Unauthenticated private val okHttpClient: OkHttpClient, @@ -63,7 +66,7 @@ internal class DefaultFileService @Inject constructor( return GlobalScope.launch(coroutineDispatchers.main) { withContext(coroutineDispatchers.io) { Try { - val folder = File(cacheDirectory, "MF") + val folder = File(sessionCacheDirectory, "MF") if (!folder.exists()) { folder.mkdirs() } @@ -102,7 +105,7 @@ internal class DefaultFileService @Inject constructor( private fun copyFile(file: File, downloadMode: FileService.DownloadMode): File { return when (downloadMode) { FileService.DownloadMode.TO_EXPORT -> file.copyTo(File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), file.name), true) - FileService.DownloadMode.FOR_EXTERNAL_SHARE -> file.copyTo(File(File(context.cacheDir, "ext_share"), file.name), true) + FileService.DownloadMode.FOR_EXTERNAL_SHARE -> file.copyTo(File(File(cacheDirectory, "ext_share"), file.name), true) FileService.DownloadMode.FOR_INTERNAL_USE -> file } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt index 5e1a524be6..1c73a76a07 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt @@ -178,14 +178,14 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter Timber.v("Encrypt file") notifyTracker(params) { contentUploadStateTracker.setEncrypting(it) } - val encryptionResult = MXEncryptedAttachments.encryptAttachment(FileInputStream(attachmentFile), attachment.mimeType) + val encryptionResult = MXEncryptedAttachments.encryptAttachment(FileInputStream(attachmentFile), attachment.getSafeMimeType()) uploadedFileEncryptedFileInfo = encryptionResult.encryptedFileInfo fileUploader .uploadByteArray(encryptionResult.encryptedByteArray, attachment.name, "application/octet-stream", progressListener) } else { fileUploader - .uploadFile(attachmentFile, attachment.name, attachment.mimeType, progressListener) + .uploadFile(attachmentFile, attachment.name, attachment.getSafeMimeType(), progressListener) } handleSuccess(params, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index 51de36291d..198a65a2d3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -261,7 +261,7 @@ internal class LocalEchoEventFactory @Inject constructor( msgType = MessageType.MSGTYPE_IMAGE, body = attachment.name ?: "image", info = ImageInfo( - mimeType = attachment.mimeType, + mimeType = attachment.getSafeMimeType(), width = width?.toInt() ?: 0, height = height?.toInt() ?: 0, size = attachment.size.toInt() @@ -293,7 +293,7 @@ internal class LocalEchoEventFactory @Inject constructor( msgType = MessageType.MSGTYPE_VIDEO, body = attachment.name ?: "video", videoInfo = VideoInfo( - mimeType = attachment.mimeType, + mimeType = attachment.getSafeMimeType(), width = width, height = height, size = attachment.size, @@ -312,7 +312,7 @@ internal class LocalEchoEventFactory @Inject constructor( msgType = MessageType.MSGTYPE_AUDIO, body = attachment.name ?: "audio", audioInfo = AudioInfo( - mimeType = attachment.mimeType?.takeIf { it.isNotBlank() } ?: "audio/mpeg", + mimeType = attachment.getSafeMimeType()?.takeIf { it.isNotBlank() } ?: "audio/mpeg", size = attachment.size ), url = attachment.path @@ -325,7 +325,7 @@ internal class LocalEchoEventFactory @Inject constructor( msgType = MessageType.MSGTYPE_FILE, body = attachment.name ?: "file", info = FileInfo( - mimeType = attachment.mimeType?.takeIf { it.isNotBlank() } + mimeType = attachment.getSafeMimeType()?.takeIf { it.isNotBlank() } ?: "application/octet-stream", size = attachment.size ), diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/Extensions.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/Extensions.kt index 3bd47baa89..40fcc0aa92 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/Extensions.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/Extensions.kt @@ -23,6 +23,6 @@ import im.vector.matrix.android.api.session.content.ContentAttachmentData */ fun ContentAttachmentData.isEditable(): Boolean { return type == ContentAttachmentData.Type.IMAGE - && mimeType?.startsWith("image/") == true - && mimeType != "image/gif" + && getSafeMimeType()?.startsWith("image/") == true + && getSafeMimeType() != "image/gif" } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/EventSharedAction.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/EventSharedAction.kt index 0b648eabdb..b9e2ab2093 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/EventSharedAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/EventSharedAction.kt @@ -18,7 +18,7 @@ package im.vector.riotx.features.home.room.detail.timeline.action import androidx.annotation.DrawableRes import androidx.annotation.StringRes -import im.vector.matrix.android.api.session.room.model.message.MessageEncryptedContent +import im.vector.matrix.android.api.session.room.model.message.MessageWithAttachmentContent import im.vector.riotx.R import im.vector.riotx.core.platform.VectorSharedAction import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData @@ -47,7 +47,7 @@ sealed class EventSharedAction(@StringRes val titleRes: Int, data class Reply(val eventId: String) : EventSharedAction(R.string.reply, R.drawable.ic_reply) - data class Share(val eventId: String, val messageContent: MessageEncryptedContent) : + data class Share(val eventId: String, val messageContent: MessageWithAttachmentContent) : EventSharedAction(R.string.share, R.drawable.ic_share) data class Resend(val eventId: String) : diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index a508761a7e..0693df2fb0 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -29,7 +29,7 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.isTextMessage import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.message.MessageContent -import im.vector.matrix.android.api.session.room.model.message.MessageEncryptedContent +import im.vector.matrix.android.api.session.room.model.message.MessageWithAttachmentContent import im.vector.matrix.android.api.session.room.model.message.MessageFormat import im.vector.matrix.android.api.session.room.model.message.MessageTextContent import im.vector.matrix.android.api.session.room.model.message.MessageType @@ -261,7 +261,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } if (canShare(msgType)) { - if (messageContent is MessageEncryptedContent) { + if (messageContent is MessageWithAttachmentContent) { add(EventSharedAction.Share(timelineEvent.eventId, messageContent)) } }