From 5f14516dec3b67286e26c5f9fec0e3df207c2215 Mon Sep 17 00:00:00 2001 From: onurays Date: Tue, 3 Mar 2020 10:39:24 +0100 Subject: [PATCH 01/12] Share images from clear and encrypted rooms. --- .../android/api/session/file/FileService.kt | 6 +- .../internal/session/DefaultFileService.kt | 73 ++++++++----------- .../session/content/UploadContentWorker.kt | 5 +- .../session/room/send/DefaultSendService.kt | 2 +- .../features/attachments/AttachmentsHelper.kt | 9 ++- .../home/room/detail/RoomDetailFragment.kt | 38 ++++------ .../timeline/action/EventSharedAction.kt | 3 +- .../action/MessageActionsViewModel.kt | 6 +- .../features/share/IncomingShareFragment.kt | 3 +- .../src/main/res/xml/riotx_provider_paths.xml | 4 + 10 files changed, 69 insertions(+), 80 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/file/FileService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/file/FileService.kt index 4d9cff3e92..32fb1a6ab0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/file/FileService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/file/FileService.kt @@ -34,7 +34,11 @@ interface FileService { /** * Download file in cache */ - FOR_INTERNAL_USE + FOR_INTERNAL_USE, + /** + * Download file in file provider path + */ + FOR_EXTERNAL_SHARE } /** 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 cf7e1b1d83..41a246397b 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,10 +25,10 @@ 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.SessionCacheDirectory +import im.vector.matrix.android.internal.di.SessionFilesDirectory import im.vector.matrix.android.internal.di.Unauthenticated import im.vector.matrix.android.internal.extensions.foldToCallback import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers -import im.vector.matrix.android.internal.util.md5 import im.vector.matrix.android.internal.util.toCancelable import im.vector.matrix.android.internal.util.writeToFile import kotlinx.coroutines.GlobalScope @@ -44,6 +44,8 @@ import javax.inject.Inject internal class DefaultFileService @Inject constructor( @SessionCacheDirectory private val cacheDirectory: File, + @SessionFilesDirectory + private val filesDirectory: File, private val contentUrlResolver: ContentUrlResolver, @Unauthenticated private val okHttpClient: OkHttpClient, @@ -62,60 +64,47 @@ internal class DefaultFileService @Inject constructor( return GlobalScope.launch(coroutineDispatchers.main) { withContext(coroutineDispatchers.io) { Try { - val folder = getFolder(downloadMode, id) - + val folder = File(cacheDirectory, "MF") + if (!folder.exists()) { + folder.mkdirs() + } File(folder, fileName) }.flatMap { destFile -> - if (!destFile.exists() || downloadMode == FileService.DownloadMode.TO_EXPORT) { - Try { - val resolvedUrl = contentUrlResolver.resolveFullSize(url) ?: throw IllegalArgumentException("url is null") + if (!destFile.exists()) { + val resolvedUrl = contentUrlResolver.resolveFullSize(url) ?: throw IllegalArgumentException("url is null") - val request = Request.Builder() - .url(resolvedUrl) - .build() + val request = Request.Builder() + .url(resolvedUrl) + .build() - val response = okHttpClient.newCall(request).execute() - var inputStream = response.body?.byteStream() - Timber.v("Response size ${response.body?.contentLength()} - Stream available: ${inputStream?.available()}") - if (!response.isSuccessful - || inputStream == null) { - throw IOException() - } - - if (elementToDecrypt != null) { - Timber.v("## decrypt file") - inputStream = MXEncryptedAttachments.decryptAttachment(inputStream, elementToDecrypt) - ?: throw IllegalStateException("Decryption error") - } - - writeToFile(inputStream, destFile) - destFile + val response = okHttpClient.newCall(request).execute() + var inputStream = response.body?.byteStream() + Timber.v("Response size ${response.body?.contentLength()} - Stream available: ${inputStream?.available()}") + if (!response.isSuccessful || inputStream == null) { + return@flatMap Try.Failure(IOException()) } - } else { - Try.just(destFile) + + if (elementToDecrypt != null) { + Timber.v("## decrypt file") + inputStream = MXEncryptedAttachments.decryptAttachment(inputStream, elementToDecrypt) + ?: throw IllegalStateException("Decryption error") + } + + writeToFile(inputStream, destFile) } + + Try.just(copyFile(destFile, downloadMode)) } } .foldToCallback(callback) }.toCancelable() } - private fun getFolder(downloadMode: FileService.DownloadMode, id: String): File { + private fun copyFile(file: File, downloadMode: FileService.DownloadMode): File { return when (downloadMode) { - FileService.DownloadMode.FOR_INTERNAL_USE -> { - // Create dir tree (MF stands for Matrix File): - // //MF// - val tmpFolderSession = File(cacheDirectory, "MF") - File(tmpFolderSession, id.md5()) - } - FileService.DownloadMode.TO_EXPORT -> { - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) - } + FileService.DownloadMode.TO_EXPORT -> file.copyTo(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), true) + FileService.DownloadMode.FOR_INTERNAL_USE -> file.copyTo(File(filesDirectory, "ext_share"), true) + FileService.DownloadMode.FOR_EXTERNAL_SHARE -> file } - .also { folder -> - if (!folder.exists()) { - folder.mkdirs() - } - } } } 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 94bdb11edb..5e1a524be6 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 @@ -205,7 +205,10 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter return Result.success( WorkerParamsFactory.toData( - params.copy( + MultipleEventSendingDispatcherWorker.Params( + sessionId = params.sessionId, + events = params.events, + isEncrypted = params.isRoomEncrypted, lastFailureMessage = failure.localizedMessage ) ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt index a99337695a..58f4d4fc5e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt @@ -233,7 +233,7 @@ internal class DefaultSendService @AssistedInject constructor( val dispatcherWork = createMultipleEventDispatcherWork(isRoomEncrypted) workManagerProvider.workManager - .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork) + .beginWith(uploadWork) .then(dispatcherWork) .enqueue() .also { operation -> diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsHelper.kt b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsHelper.kt index c9ee1cb6e5..ba1197b787 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsHelper.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsHelper.kt @@ -27,6 +27,7 @@ import com.kbeanie.multipicker.api.Picker.PICK_IMAGE_CAMERA import com.kbeanie.multipicker.api.Picker.PICK_IMAGE_DEVICE import com.kbeanie.multipicker.core.ImagePickerImpl import com.kbeanie.multipicker.core.PickerManager +import com.kbeanie.multipicker.utils.IntentUtils import im.vector.matrix.android.BuildConfig import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.riotx.core.platform.Restorable @@ -176,13 +177,13 @@ class AttachmentsHelper private constructor(private val context: Context, fun handleShareIntent(intent: Intent): Boolean { val type = intent.resolveType(context) ?: return false if (type.startsWith("image")) { - imagePicker.submit(intent) + imagePicker.submit(IntentUtils.getPickerIntentForSharing(intent)) } else if (type.startsWith("video")) { - videoPicker.submit(intent) + videoPicker.submit(IntentUtils.getPickerIntentForSharing(intent)) } else if (type.startsWith("audio")) { - videoPicker.submit(intent) + videoPicker.submit(IntentUtils.getPickerIntentForSharing(intent)) } else if (type.startsWith("application") || type.startsWith("file") || type.startsWith("*")) { - filePicker.submit(intent) + filePicker.submit(IntentUtils.getPickerIntentForSharing(intent)) } else { return false } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index c2eb61b3ca..80548cbe71 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -57,17 +57,17 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import com.github.piasy.biv.BigImageViewer -import com.github.piasy.biv.loader.ImageLoader import com.google.android.material.checkbox.MaterialCheckBox import com.google.android.material.snackbar.Snackbar import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputLayout import com.jakewharton.rxbinding3.widget.textChanges +import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.permalinks.PermalinkFactory import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.file.FileService import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent import im.vector.matrix.android.api.session.room.model.message.MessageContent @@ -77,12 +77,14 @@ import im.vector.matrix.android.api.session.room.model.message.MessageImageInfoC import im.vector.matrix.android.api.session.room.model.message.MessageTextContent import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent +import im.vector.matrix.android.api.session.room.model.message.getFileUrl import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent import im.vector.matrix.android.api.util.MatrixItem import im.vector.matrix.android.api.util.toMatrixItem +import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt import im.vector.riotx.R import im.vector.riotx.core.dialogs.withColoredButton import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer @@ -1145,30 +1147,18 @@ class RoomDetailFragment @Inject constructor( promptConfirmationToRedactEvent(action) } is EventSharedAction.Share -> { - // TODO current data communication is too limited - // Need to now the media type - // TODO bad, just POC - BigImageViewer.imageLoader().loadImage( - action.hashCode(), - Uri.parse(action.imageUrl), - object : ImageLoader.Callback { - override fun onFinish() {} - - override fun onSuccess(image: File?) { - if (image != null) { - shareMedia(requireContext(), image, "image/*") + session.downloadFile( + FileService.DownloadMode.FOR_EXTERNAL_SHARE, + action.eventId, + action.messageContent.body, + action.messageContent.getFileUrl(), + action.messageContent.encryptedFileInfo?.toElementToDecrypt(), + object : MatrixCallback { + override fun onSuccess(data: File) { + if (isAdded) { + shareMedia(requireContext(), data, "image/*") } } - - override fun onFail(error: Exception?) {} - - override fun onCacheHit(imageType: Int, image: File?) {} - - override fun onCacheMiss(imageType: Int, image: File?) {} - - override fun onProgress(progress: Int) {} - - override fun onStart() {} } ) } 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 cba89d8481..ec1f43feb0 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,6 +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.MessageImageContent import im.vector.riotx.R import im.vector.riotx.core.platform.VectorSharedAction import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData @@ -46,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 imageUrl: String) : + data class Share(val eventId: String, val messageContent: MessageImageContent) : 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 fa9bdbc29c..1e14f5292b 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 @@ -262,11 +262,9 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted if (canShare(msgType)) { if (messageContent is MessageImageContent) { - session.contentUrlResolver().resolveFullSize(messageContent.url)?.let { url -> - add(EventSharedAction.Share(url)) - } + add(EventSharedAction.Share(timelineEvent.eventId, messageContent)) } - // TODO + // TODO Support other media types } if (timelineEvent.root.sendState == SendState.SENT) { diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt index eb0f5128ba..74821ab2fe 100644 --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt @@ -28,7 +28,6 @@ import androidx.appcompat.widget.SearchView import androidx.core.view.isVisible import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import com.kbeanie.multipicker.utils.IntentUtils import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.riotx.R @@ -78,7 +77,7 @@ class IncomingShareFragment @Inject constructor( val intent = vectorBaseActivity.intent val isShareManaged = when (intent?.action) { Intent.ACTION_SEND -> { - var isShareManaged = attachmentsHelper.handleShareIntent(IntentUtils.getPickerIntentForSharing(intent)) + var isShareManaged = attachmentsHelper.handleShareIntent(intent) if (!isShareManaged) { isShareManaged = handleTextShare(intent) } diff --git a/vector/src/main/res/xml/riotx_provider_paths.xml b/vector/src/main/res/xml/riotx_provider_paths.xml index 7d3fcb2203..2069cf3191 100644 --- a/vector/src/main/res/xml/riotx_provider_paths.xml +++ b/vector/src/main/res/xml/riotx_provider_paths.xml @@ -3,4 +3,8 @@ + + \ No newline at end of file From c4f2eeeab7637b341a1381d284676f78ea73bb06 Mon Sep 17 00:00:00 2001 From: onurays Date: Tue, 3 Mar 2020 10:40:58 +0100 Subject: [PATCH 02/12] Changelog added. --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 4f8838deb6..28fa27932f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ Improvements 🙌: Bugfix 🐛: - Fix crash on attachment preview screen (#1088) + - "Share" option is not appearing in encrypted rooms for images (#1031) Translations 🗣: - From 319667096f1d3dbd6843b3161c29275a751e42f6 Mon Sep 17 00:00:00 2001 From: onurays Date: Tue, 3 Mar 2020 13:41:40 +0100 Subject: [PATCH 03/12] Return Try.Failure instead of throwing exception. --- .../matrix/android/internal/session/DefaultFileService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 41a246397b..ba6f9543e7 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 @@ -87,7 +87,7 @@ internal class DefaultFileService @Inject constructor( if (elementToDecrypt != null) { Timber.v("## decrypt file") inputStream = MXEncryptedAttachments.decryptAttachment(inputStream, elementToDecrypt) - ?: throw IllegalStateException("Decryption error") + ?: return@flatMap Try.Failure(IllegalStateException("Decryption error")) } writeToFile(inputStream, destFile) From d09ac8fbce1068afe43aea729e618a6287875e0c Mon Sep 17 00:00:00 2001 From: onurays Date: Tue, 3 Mar 2020 14:54:05 +0100 Subject: [PATCH 04/12] Try to show full image as the fallback of the thumbnail. --- .../features/media/ImageContentRenderer.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt index e497d9ce04..3f0daebdbe 100644 --- a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt +++ b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt @@ -127,6 +127,23 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: GlideApp .with(imageView) .load(resolvedUrl) + .listener(object : RequestListener { + override fun onResourceReady(resource: Drawable?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean { + return false + } + + override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, isFirstResource: Boolean): Boolean { + data.url + ?.takeIf { it != resolvedUrl } + ?.let { + GlideApp + .with(imageView) + .load(it) + return false + } + return true + } + }) } } From 34c5537436555c8c3579a393aef1f0a5857060ea Mon Sep 17 00:00:00 2001 From: onurays Date: Tue, 3 Mar 2020 16:42:33 +0100 Subject: [PATCH 05/12] Fix fallback to full image. --- .../features/media/ImageContentRenderer.kt | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt index 3f0daebdbe..6f34001124 100644 --- a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt +++ b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt @@ -127,23 +127,18 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: GlideApp .with(imageView) .load(resolvedUrl) - .listener(object : RequestListener { - override fun onResourceReady(resource: Drawable?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean { - return false - } - - override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, isFirstResource: Boolean): Boolean { - data.url - ?.takeIf { it != resolvedUrl } - ?.let { - GlideApp - .with(imageView) - .load(it) - return false - } - return true - } - }) + .apply { + contentUrlResolver + .resolveFullSize(data.url) + ?.takeIf { it != resolvedUrl } + ?.let { fullSizeUrl -> + error( + GlideApp + .with(imageView) + .load(fullSizeUrl) + ) + } + } } } From 7158554ee2aef1543b276d93303fc1948022af5c Mon Sep 17 00:00:00 2001 From: onurays Date: Tue, 3 Mar 2020 16:56:22 +0100 Subject: [PATCH 06/12] Add fallback logic only if the mode is thumbnail. --- .../features/media/ImageContentRenderer.kt | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt index 6f34001124..737549d5b8 100644 --- a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt +++ b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt @@ -128,16 +128,13 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: .with(imageView) .load(resolvedUrl) .apply { - contentUrlResolver - .resolveFullSize(data.url) - ?.takeIf { it != resolvedUrl } - ?.let { fullSizeUrl -> - error( - GlideApp - .with(imageView) - .load(fullSizeUrl) - ) - } + if (mode == Mode.THUMBNAIL) { + error( + GlideApp + .with(imageView) + .load(contentUrlResolver.resolveFullSize(data.url)) + ) + } } } } From 26d387cc125d566313f8b71dc2f8b54fe0b77863 Mon Sep 17 00:00:00 2001 From: onurays Date: Wed, 4 Mar 2020 13:47:48 +0100 Subject: [PATCH 07/12] Support sharing other media types. --- .../internal/session/DefaultFileService.kt | 13 ++++--- .../home/room/detail/RoomDetailFragment.kt | 34 +++++++++++-------- .../timeline/action/EventSharedAction.kt | 4 +-- .../action/MessageActionsViewModel.kt | 10 +++--- .../src/main/res/xml/riotx_provider_paths.xml | 4 --- 5 files changed, 33 insertions(+), 32 deletions(-) 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 ba6f9543e7..a5af4a4a7b 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 @@ -16,6 +16,7 @@ package im.vector.matrix.android.internal.session +import android.content.Context import android.os.Environment import arrow.core.Try import im.vector.matrix.android.api.MatrixCallback @@ -25,7 +26,6 @@ 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.SessionCacheDirectory -import im.vector.matrix.android.internal.di.SessionFilesDirectory import im.vector.matrix.android.internal.di.Unauthenticated import im.vector.matrix.android.internal.extensions.foldToCallback import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers @@ -42,10 +42,9 @@ import java.io.IOException import javax.inject.Inject internal class DefaultFileService @Inject constructor( + private val context: Context, @SessionCacheDirectory private val cacheDirectory: File, - @SessionFilesDirectory - private val filesDirectory: File, private val contentUrlResolver: ContentUrlResolver, @Unauthenticated private val okHttpClient: OkHttpClient, @@ -71,7 +70,7 @@ internal class DefaultFileService @Inject constructor( File(folder, fileName) }.flatMap { destFile -> if (!destFile.exists()) { - val resolvedUrl = contentUrlResolver.resolveFullSize(url) ?: throw IllegalArgumentException("url is null") + val resolvedUrl = contentUrlResolver.resolveFullSize(url) ?: return@flatMap Try.Failure(IllegalArgumentException("url is null")) val request = Request.Builder() .url(resolvedUrl) @@ -102,9 +101,9 @@ internal class DefaultFileService @Inject constructor( private fun copyFile(file: File, downloadMode: FileService.DownloadMode): File { return when (downloadMode) { - FileService.DownloadMode.TO_EXPORT -> file.copyTo(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), true) - FileService.DownloadMode.FOR_INTERNAL_USE -> file.copyTo(File(filesDirectory, "ext_share"), true) - FileService.DownloadMode.FOR_EXTERNAL_SHARE -> file + 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_INTERNAL_USE -> file } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 80548cbe71..ad4e9694db 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -37,6 +37,7 @@ import androidx.annotation.StringRes import androidx.appcompat.app.AlertDialog import androidx.core.app.ActivityOptionsCompat import androidx.core.content.ContextCompat +import androidx.core.net.toUri import androidx.core.text.buildSpannedString import androidx.core.util.Pair import androidx.core.view.ViewCompat @@ -95,6 +96,7 @@ import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.extensions.showKeyboard import im.vector.riotx.core.files.addEntryToDownloadManager import im.vector.riotx.core.glide.GlideApp +import im.vector.riotx.core.intent.getMimeTypeFromUri import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.resources.ColorProvider import im.vector.riotx.core.ui.views.JumpToReadMarkerView @@ -1126,6 +1128,23 @@ class RoomDetailFragment @Inject constructor( roomDetailViewModel.handle(RoomDetailAction.EnterTrackingUnreadMessagesState) } + private fun onShareActionClicked(action: EventSharedAction.Share) { + session.downloadFile( + FileService.DownloadMode.FOR_EXTERNAL_SHARE, + action.eventId, + action.messageContent.body, + action.messageContent.getFileUrl(), + action.messageContent.encryptedFileInfo?.toElementToDecrypt(), + object : MatrixCallback { + override fun onSuccess(data: File) { + if (isAdded) { + shareMedia(requireContext(), data, getMimeTypeFromUri(requireContext(), data.toUri())) + } + } + } + ) + } + private fun handleActions(action: EventSharedAction) { when (action) { is EventSharedAction.OpenUserProfile -> { @@ -1147,20 +1166,7 @@ class RoomDetailFragment @Inject constructor( promptConfirmationToRedactEvent(action) } is EventSharedAction.Share -> { - session.downloadFile( - FileService.DownloadMode.FOR_EXTERNAL_SHARE, - action.eventId, - action.messageContent.body, - action.messageContent.getFileUrl(), - action.messageContent.encryptedFileInfo?.toElementToDecrypt(), - object : MatrixCallback { - override fun onSuccess(data: File) { - if (isAdded) { - shareMedia(requireContext(), data, "image/*") - } - } - } - ) + onShareActionClicked(action) } is EventSharedAction.ViewEditHistory -> { onEditedDecorationClicked(action.messageInformationData) 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 ec1f43feb0..0b648eabdb 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.MessageImageContent +import im.vector.matrix.android.api.session.room.model.message.MessageEncryptedContent 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: MessageImageContent) : + data class Share(val eventId: String, val messageContent: MessageEncryptedContent) : 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 1e14f5292b..a508761a7e 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,8 +29,8 @@ 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.MessageFormat -import im.vector.matrix.android.api.session.room.model.message.MessageImageContent import im.vector.matrix.android.api.session.room.model.message.MessageTextContent import im.vector.matrix.android.api.session.room.model.message.MessageType import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent @@ -261,10 +261,9 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } if (canShare(msgType)) { - if (messageContent is MessageImageContent) { + if (messageContent is MessageEncryptedContent) { add(EventSharedAction.Share(timelineEvent.eventId, messageContent)) } - // TODO Support other media types } if (timelineEvent.root.sendState == SendState.SENT) { @@ -372,8 +371,9 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted return when (msgType) { MessageType.MSGTYPE_IMAGE, MessageType.MSGTYPE_AUDIO, - MessageType.MSGTYPE_VIDEO -> true - else -> false + MessageType.MSGTYPE_VIDEO, + MessageType.MSGTYPE_FILE -> true + else -> false } } } diff --git a/vector/src/main/res/xml/riotx_provider_paths.xml b/vector/src/main/res/xml/riotx_provider_paths.xml index 2069cf3191..7d3fcb2203 100644 --- a/vector/src/main/res/xml/riotx_provider_paths.xml +++ b/vector/src/main/res/xml/riotx_provider_paths.xml @@ -3,8 +3,4 @@ - - \ No newline at end of file From d4384328fe15391c822be3adb4c00bf59104ad76 Mon Sep 17 00:00:00 2001 From: onurays Date: Wed, 4 Mar 2020 16:26:09 +0100 Subject: [PATCH 08/12] Use "image/jpeg" instead of "image/jpg" --- CHANGES.md | 3 ++- .../android/api/session/content/ContentAttachmentData.kt | 2 ++ .../session/room/model/message/MessageAudioContent.kt | 2 +- .../api/session/room/model/message/MessageFileContent.kt | 2 +- .../room/model/message/MessageImageInfoContent.kt | 2 +- .../session/room/model/message/MessageVideoContent.kt | 2 +- ...cryptedContent.kt => MessageWithAttachmentContent.kt} | 4 ++-- .../vector/matrix/android/internal/di/FileQualifiers.kt | 4 ++++ .../vector/matrix/android/internal/di/MatrixComponent.kt | 4 ++++ .../im/vector/matrix/android/internal/di/MatrixModule.kt | 8 ++++++++ .../android/internal/session/DefaultFileService.kt | 9 ++++++--- .../internal/session/content/UploadContentWorker.kt | 4 ++-- .../internal/session/room/send/LocalEchoEventFactory.kt | 8 ++++---- .../riotx/features/attachments/preview/Extensions.kt | 4 ++-- .../room/detail/timeline/action/EventSharedAction.kt | 4 ++-- .../detail/timeline/action/MessageActionsViewModel.kt | 4 ++-- 16 files changed, 44 insertions(+), 22 deletions(-) rename matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/{MessageEncryptedContent.kt => MessageWithAttachmentContent.kt} (89%) 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)) } } From 9cbaadedfb496a3f17f2c35b16b364f1c77f5f61 Mon Sep 17 00:00:00 2001 From: onurays Date: Wed, 4 Mar 2020 16:52:08 +0100 Subject: [PATCH 09/12] Unused context parameter is removed. --- .../vector/matrix/android/internal/session/DefaultFileService.kt | 1 - 1 file changed, 1 deletion(-) 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 37744209a9..e637d9b3a7 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 @@ -43,7 +43,6 @@ import java.io.IOException import javax.inject.Inject internal class DefaultFileService @Inject constructor( - private val context: Context, @CacheDirectory private val cacheDirectory: File, @SessionCacheDirectory From b7ad50a3ceeadd1f19e48ae8494aab8e9560d1e6 Mon Sep 17 00:00:00 2001 From: onurays Date: Wed, 4 Mar 2020 16:52:52 +0100 Subject: [PATCH 10/12] Make mimeType private to encourage using getSafeMimeType() method. --- .../matrix/android/api/session/content/ContentAttachmentData.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f78cb7e882..e32bb9f21f 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 @@ -31,7 +31,7 @@ data class ContentAttachmentData( val name: String? = null, val queryUri: String, val path: String, - val mimeType: String?, + private val mimeType: String?, val type: Type ) : Parcelable { From 03d2cd063959c66dcc551303ec93e6742d50eecf Mon Sep 17 00:00:00 2001 From: onurays Date: Wed, 4 Mar 2020 17:41:41 +0100 Subject: [PATCH 11/12] Lint fix. --- .../android/internal/session/DefaultFileService.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 e637d9b3a7..9eaea8cc34 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 @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session -import android.content.Context import android.os.Environment import arrow.core.Try import im.vector.matrix.android.api.MatrixCallback @@ -103,9 +102,12 @@ 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(cacheDirectory, "ext_share"), file.name), true) - FileService.DownloadMode.FOR_INTERNAL_USE -> file + FileService.DownloadMode.TO_EXPORT -> + file.copyTo(File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), file.name), true) + FileService.DownloadMode.FOR_EXTERNAL_SHARE -> + file.copyTo(File(File(cacheDirectory, "ext_share"), file.name), true) + FileService.DownloadMode.FOR_INTERNAL_USE -> + file } } } From 05683967c042b17edf03bd161d2f9f4a6a190266 Mon Sep 17 00:00:00 2001 From: onurays Date: Fri, 6 Mar 2020 12:07:38 +0100 Subject: [PATCH 12/12] Code review fixes. --- matrix-sdk-android/build.gradle | 2 +- .../DefaultContentUploadStateTracker.kt | 17 +++++++++++++-- .../session/content/UploadContentWorker.kt | 21 +++++++++---------- .../session/room/send/DefaultSendService.kt | 2 +- vector/build.gradle | 2 +- .../action/MessageActionsViewModel.kt | 6 ++---- 6 files changed, 30 insertions(+), 20 deletions(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 062b590acf..f980279a8d 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -126,7 +126,7 @@ dependencies { kapt 'dk.ilios:realmfieldnameshelper:1.1.1' // Work - implementation "androidx.work:work-runtime-ktx:2.3.0" + implementation "androidx.work:work-runtime-ktx:2.3.3" // FP implementation "io.arrow-kt:arrow-core:$arrow_version" diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt index 66a8341801..7a13d5f8a5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt @@ -20,6 +20,7 @@ import android.os.Handler import android.os.Looper import im.vector.matrix.android.api.session.content.ContentUploadStateTracker import im.vector.matrix.android.internal.session.SessionScope +import timber.log.Timber import javax.inject.Inject @SessionScope @@ -33,7 +34,13 @@ internal class DefaultContentUploadStateTracker @Inject constructor() : ContentU val listeners = listeners.getOrPut(key) { ArrayList() } listeners.add(updateListener) val currentState = states[key] ?: ContentUploadStateTracker.State.Idle - mainHandler.post { updateListener.onUpdate(currentState) } + mainHandler.post { + try { + updateListener.onUpdate(currentState) + } catch (e: Exception) { + Timber.e(e, "## ContentUploadStateTracker.onUpdate() failed") + } + } } override fun untrack(key: String, updateListener: ContentUploadStateTracker.UpdateListener) { @@ -79,7 +86,13 @@ internal class DefaultContentUploadStateTracker @Inject constructor() : ContentU private fun updateState(key: String, state: ContentUploadStateTracker.State) { states[key] = state mainHandler.post { - listeners[key]?.forEach { it.onUpdate(state) } + listeners[key]?.forEach { + try { + it.onUpdate(state) + } catch (e: Exception) { + Timber.e(e, "## ContentUploadStateTracker.onUpdate() failed") + } + } } } } 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 1c73a76a07..1dde25fd78 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 @@ -58,7 +58,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter override val sessionId: String, val events: List, val attachment: ContentAttachmentData, - val isRoomEncrypted: Boolean, + val isEncrypted: Boolean, val compressBeforeSending: Boolean, override val lastFailureMessage: String? = null ) : SessionWorkerParams @@ -90,9 +90,11 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter Timber.e(e) notifyTracker(params) { contentUploadStateTracker.setFailure(it, e) } return Result.success( - WorkerParamsFactory.toData(params.copy( - lastFailureMessage = e.localizedMessage - )) + WorkerParamsFactory.toData( + params.copy( + lastFailureMessage = e.localizedMessage + ) + ) ) } .let { originalFile -> @@ -136,7 +138,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter } try { - val contentUploadResponse = if (params.isRoomEncrypted) { + val contentUploadResponse = if (params.isEncrypted) { Timber.v("Encrypt thumbnail") notifyTracker(params) { contentUploadStateTracker.setEncryptingThumbnail(it) } val encryptionResult = MXEncryptedAttachments.encryptAttachment(ByteArrayInputStream(thumbnailData.bytes), thumbnailData.mimeType) @@ -174,7 +176,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter var uploadedFileEncryptedFileInfo: EncryptedFileInfo? = null return try { - val contentUploadResponse = if (params.isRoomEncrypted) { + val contentUploadResponse = if (params.isEncrypted) { Timber.v("Encrypt file") notifyTracker(params) { contentUploadStateTracker.setEncrypting(it) } @@ -205,10 +207,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter return Result.success( WorkerParamsFactory.toData( - MultipleEventSendingDispatcherWorker.Params( - sessionId = params.sessionId, - events = params.events, - isEncrypted = params.isRoomEncrypted, + params.copy( lastFailureMessage = failure.localizedMessage ) ) @@ -229,7 +228,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter updateEvent(it, attachmentUrl, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo, newImageAttributes) } - val sendParams = MultipleEventSendingDispatcherWorker.Params(params.sessionId, updatedEvents, params.isRoomEncrypted) + val sendParams = MultipleEventSendingDispatcherWorker.Params(params.sessionId, updatedEvents, params.isEncrypted) return Result.success(WorkerParamsFactory.toData(sendParams)) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt index 58f4d4fc5e..a99337695a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt @@ -233,7 +233,7 @@ internal class DefaultSendService @AssistedInject constructor( val dispatcherWork = createMultipleEventDispatcherWork(isRoomEncrypted) workManagerProvider.workManager - .beginWith(uploadWork) + .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork) .then(dispatcherWork) .enqueue() .also { operation -> diff --git a/vector/build.gradle b/vector/build.gradle index 988712d87e..6d24a26838 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -296,7 +296,7 @@ dependencies { implementation 'com.airbnb.android:mvrx:1.3.0' // Work - implementation "androidx.work:work-runtime-ktx:2.3.0-beta02" + implementation "androidx.work:work-runtime-ktx:2.3.3" // Paging implementation "androidx.paging:paging-runtime-ktx:2.1.1" 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 0693df2fb0..1fe1db27d7 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 @@ -260,10 +260,8 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted add(EventSharedAction.ViewEditHistory(informationData)) } - if (canShare(msgType)) { - if (messageContent is MessageWithAttachmentContent) { - add(EventSharedAction.Share(timelineEvent.eventId, messageContent)) - } + if (canShare(msgType) && messageContent is MessageWithAttachmentContent) { + add(EventSharedAction.Share(timelineEvent.eventId, messageContent)) } if (timelineEvent.root.sendState == SendState.SENT) {