From cc7d71af80918ce8782fa61246278d2f75c21709 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 17 May 2023 08:44:35 +0200 Subject: [PATCH] Media : branch upload to preview screen (need improvement) --- .../preview/AttachmentsPreviewEvents.kt | 13 ++- .../preview/AttachmentsPreviewPresenter.kt | 63 ++++++++++ .../preview/AttachmentsPreviewState.kt | 3 + .../AttachmentsPreviewStateProvider.kt | 14 ++- .../preview/AttachmentsPreviewView.kt | 108 ++++++++++++++---- .../preview/error/ErrorFormatter.kt | 30 +++++ .../media/local/AndroidLocalMediaFactory.kt | 3 +- .../impl/media/local/FakeLocalMediaFactory.kt | 5 +- .../messages/impl/media/local/LocalMedia.kt | 2 +- .../impl/media/local/LocalMediaFactory.kt | 5 + .../textcomposer/MessageComposerPresenter.kt | 29 ----- .../libraries/core/extensions/Result.kt | 13 +++ .../mediaupload/api/MediaPreProcessor.kt | 4 +- ...sorImpl.kt => AndroidMediaPreProcessor.kt} | 34 +++--- .../mediaupload/test/FakeMediaPreProcessor.kt | 3 +- 15 files changed, 237 insertions(+), 92 deletions(-) rename libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaType.kt => features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvents.kt (68%) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/error/ErrorFormatter.kt rename libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/{MediaPreProcessorImpl.kt => AndroidMediaPreProcessor.kt} (90%) diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaType.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvents.kt similarity index 68% rename from libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaType.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvents.kt index e16ca43699..14a6a3fb2d 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaType.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvents.kt @@ -14,11 +14,12 @@ * limitations under the License. */ -package io.element.android.libraries.mediaupload.api +package io.element.android.features.messages.impl.attachments.preview -sealed interface MediaType { - object Image : MediaType - object Video : MediaType - object Audio : MediaType - object File : MediaType +import androidx.compose.runtime.Immutable + +@Immutable +sealed interface AttachmentsPreviewEvents { + object SendAttachment : AttachmentsPreviewEvents + object ClearSendState : AttachmentsPreviewEvents } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt index 6bce4a3bad..6400b84725 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt @@ -16,15 +16,32 @@ package io.element.android.features.messages.impl.attachments.preview +import android.net.Uri import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.features.messages.impl.attachments.Attachment +import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.executeResult +import io.element.android.libraries.core.extensions.flatMap +import io.element.android.libraries.designsystem.utils.SnackbarDispatcher +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.mediaupload.api.MediaPreProcessor +import io.element.android.libraries.mediaupload.api.sendMedia +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch class AttachmentsPreviewPresenter @AssistedInject constructor( @Assisted private val attachment: Attachment, + private val room: MatrixRoom, + private val mediaPreProcessor: MediaPreProcessor, + private val snackbarDispatcher: SnackbarDispatcher, ) : Presenter { @AssistedFactory @@ -35,8 +52,54 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( @Composable override fun present(): AttachmentsPreviewState { + val coroutineScope = rememberCoroutineScope() + + val sendActionState = remember { + mutableStateOf>(Async.Uninitialized) + } + + fun handleEvents(attachmentsPreviewEvents: AttachmentsPreviewEvents) { + when (attachmentsPreviewEvents) { + AttachmentsPreviewEvents.SendAttachment -> coroutineScope.sendAttachment(attachment, sendActionState) + AttachmentsPreviewEvents.ClearSendState -> sendActionState.value = Async.Uninitialized + } + } + return AttachmentsPreviewState( attachment = attachment, + sendActionState = sendActionState.value, + eventSink = ::handleEvents ) } + + private fun CoroutineScope.sendAttachment( + attachment: Attachment, + sendActionState: MutableState>, + ) = launch { + when (attachment) { + is Attachment.Media -> { + sendMedia( + uri = attachment.localMedia.uri, + mimeType = attachment.localMedia.mimeType, + deleteOriginal = false, + sendActionState = sendActionState + ) + } + } + } + + private suspend fun sendMedia( + uri: Uri, + mimeType: String, + deleteOriginal: Boolean = false, + sendActionState: MutableState>, + ) { + suspend { + mediaPreProcessor + .process(uri, mimeType, deleteOriginal) + .flatMap { info -> + room.sendMedia(info) + } + }.executeResult(sendActionState) + } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt index 8e1b7fca78..67350f5048 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt @@ -17,7 +17,10 @@ package io.element.android.features.messages.impl.attachments.preview import io.element.android.features.messages.impl.attachments.Attachment +import io.element.android.libraries.architecture.Async data class AttachmentsPreviewState( val attachment: Attachment, + val sendActionState: Async, + val eventSink: (AttachmentsPreviewEvents) -> Unit ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt index 122a26ab5e..151698b0f4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt @@ -20,17 +20,23 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.core.net.toUri import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.media.local.LocalMedia +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.core.mimetype.MimeTypes open class AttachmentsPreviewStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - aAttachmentsPreviewState(), + anAttachmentsPreviewState(), + anAttachmentsPreviewState(sendActionState = Async.Loading()), + anAttachmentsPreviewState(sendActionState = Async.Failure(RuntimeException())), // Add other states here ) } -fun aAttachmentsPreviewState() = AttachmentsPreviewState( +fun anAttachmentsPreviewState(sendActionState: Async = Async.Uninitialized) = AttachmentsPreviewState( attachment = Attachment.Media( - localMedia = LocalMedia("".toUri(), mimeType = null) - ) + localMedia = LocalMedia("".toUri(), mimeType = MimeTypes.OctetStream), + ), + sendActionState = sendActionState, + eventSink = {} ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt index c69f19e019..8687eb85fa 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt @@ -29,17 +29,23 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.messages.impl.attachments.Attachment +import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError import io.element.android.features.messages.impl.media.local.LocalMediaView +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.designsystem.components.ProgressDialog +import io.element.android.libraries.designsystem.components.dialogs.RetryDialog import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton +import io.element.android.libraries.ui.strings.R import io.element.android.libraries.ui.strings.R as StringsR @Composable @@ -49,39 +55,93 @@ fun AttachmentsPreviewView( modifier: Modifier = Modifier, ) { - fun onSendClicked() { + fun postSendAttachment() { + state.eventSink(AttachmentsPreviewEvents.SendAttachment) + } + + fun postClearSendState() { + state.eventSink(AttachmentsPreviewEvents.ClearSendState) + } + + if (state.sendActionState is Async.Success) { + LaunchedEffect(state.sendActionState) { + onDismiss() + } } Scaffold(modifier) { - Column( - modifier = Modifier.fillMaxWidth(), - ) { - Spacer( - modifier = Modifier.height(80.dp) + Box { + AttachmentPreviewContent( + attachment = state.attachment, + onSendClicked = ::postSendAttachment, + onDismiss = onDismiss ) - Box( - modifier = Modifier - .fillMaxWidth() - .weight(1f) - ) { - when (state.attachment) { - is Attachment.Media -> LocalMediaView( - localMedia = state.attachment.localMedia - ) - } - } - AttachmentsPreviewBottomActions( - onCancelClicked = onDismiss, - onSendClicked = ::onSendClicked, - modifier = Modifier - .fillMaxWidth() - .defaultMinSize(minHeight = 120.dp) - .padding(all = 24.dp) + AttachmentSendStateView( + sendActionState = state.sendActionState, + onRetryClicked = ::postSendAttachment, + onRetryDismissed = ::postClearSendState ) } } } +@Composable +private fun AttachmentSendStateView( + sendActionState: Async, + onRetryDismissed: () -> Unit, + onRetryClicked: () -> Unit +) { + when (sendActionState) { + is Async.Loading -> { + ProgressDialog(text = stringResource(id = R.string.common_loading)) + } + + is Async.Failure -> { + RetryDialog( + content = stringResource(sendAttachmentError(sendActionState.error)), + onDismiss = onRetryDismissed, + onRetry = onRetryClicked + ) + } + + else -> Unit + } +} + +@Composable +private fun AttachmentPreviewContent( + attachment: Attachment, + onSendClicked: () -> Unit, + onDismiss: () -> Unit +) { + Column( + modifier = Modifier.fillMaxWidth(), + ) { + Spacer( + modifier = Modifier.height(80.dp) + ) + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + when (attachment) { + is Attachment.Media -> LocalMediaView( + localMedia = attachment.localMedia + ) + } + } + AttachmentsPreviewBottomActions( + onCancelClicked = onDismiss, + onSendClicked = onSendClicked, + modifier = Modifier + .fillMaxWidth() + .defaultMinSize(minHeight = 120.dp) + .padding(all = 24.dp) + ) + } +} + @Composable private fun AttachmentsPreviewBottomActions( onCancelClicked: () -> Unit, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/error/ErrorFormatter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/error/ErrorFormatter.kt new file mode 100644 index 0000000000..92dcd21b8e --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/error/ErrorFormatter.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.messages.impl.attachments.preview.error + +import io.element.android.libraries.mediaupload.api.MediaPreProcessor +import io.element.android.libraries.ui.strings.R + +fun sendAttachmentError( + throwable: Throwable +): Int { + return if (throwable is MediaPreProcessor.Failure) { + R.string.screen_media_upload_preview_error_failed_processing + } else { + R.string.screen_media_upload_preview_error_failed_sending + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt index 00136d616e..8c7bef7cbe 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt @@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.media.local import android.content.Context import android.net.Uri import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext import javax.inject.Inject @@ -30,7 +31,7 @@ class AndroidLocalMediaFactory @Inject constructor( override fun createFromUri(uri: Uri?, mimeType: String?): LocalMedia? { if (uri == null) return null - val resolvedMimeType = mimeType ?: context.contentResolver.getType(uri) + val resolvedMimeType = mimeType ?: context.contentResolver.getType(uri) ?: MimeTypes.OctetStream return LocalMedia(uri, resolvedMimeType) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/FakeLocalMediaFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/FakeLocalMediaFactory.kt index 7f20492972..83915dd89d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/FakeLocalMediaFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/FakeLocalMediaFactory.kt @@ -17,13 +17,14 @@ package io.element.android.features.messages.impl.media.local import android.net.Uri +import io.element.android.libraries.core.mimetype.MimeTypes class FakeLocalMediaFactory() : LocalMediaFactory { - var mimeType: String? = null + var fallbackMimeType: String = MimeTypes.OctetStream override fun createFromUri(uri: Uri?, mimeType: String?): LocalMedia? { if (uri == null) return null - return LocalMedia(uri, mimeType) + return LocalMedia(uri, mimeType ?: fallbackMimeType) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMedia.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMedia.kt index a1ece7329a..c26a4baaff 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMedia.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMedia.kt @@ -23,5 +23,5 @@ import kotlinx.parcelize.Parcelize @Parcelize data class LocalMedia( val uri: Uri, - val mimeType: String?, + val mimeType: String, ) : Parcelable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaFactory.kt index 08f026c4ac..e7d2ea1ba6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaFactory.kt @@ -19,5 +19,10 @@ package io.element.android.features.messages.impl.media.local import android.net.Uri interface LocalMediaFactory { + /** + * This method will create a [LocalMedia] with the given [uri] and [mimeType] + * If the [mimeType] is null, it'll try to read it from the content. + * + */ fun createFromUri(uri: Uri?, mimeType: String?): LocalMedia? } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt index 54019b5e8a..97d4ab25ca 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt @@ -34,7 +34,6 @@ import io.element.android.libraries.core.data.StableCharSequence import io.element.android.libraries.core.data.toStableCharSequence import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.designsystem.utils.SnackbarDispatcher -import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.SingleIn import io.element.android.libraries.featureflag.api.FeatureFlagService @@ -42,15 +41,11 @@ import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.mediapickers.api.PickerProvider import io.element.android.libraries.mediaupload.api.MediaPreProcessor -import io.element.android.libraries.mediaupload.api.MediaType -import io.element.android.libraries.mediaupload.api.sendMedia import io.element.android.libraries.textcomposer.MessageComposerMode import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -import timber.log.Timber import javax.inject.Inject -import io.element.android.libraries.ui.strings.R as StringR @SingleIn(RoomScope::class) class MessageComposerPresenter @Inject constructor( @@ -59,8 +54,6 @@ class MessageComposerPresenter @Inject constructor( private val mediaPickerProvider: PickerProvider, private val featureFlagService: FeatureFlagService, private val localMediaFactory: LocalMediaFactory, - private val mediaPreProcessor: MediaPreProcessor, - private val snackbarDispatcher: SnackbarDispatcher, ) : Presenter { @SuppressLint("UnsafeOptInUsageError") @@ -186,26 +179,4 @@ class MessageComposerPresenter @Inject constructor( } } - private fun CoroutineScope.sendMedia( - uri: Uri, - mediaType: MediaType, - deleteOriginal: Boolean = false - ) = launch { - mediaPreProcessor.process(uri, mediaType, deleteOriginal) - .map { info -> - room.sendMedia(info) - } - .onSuccess { - Timber.d("onSuccess sending media") - }.onFailure { failure -> - Timber.e(failure, "onfailure sending media: $failure") - val snackbarMessage = if (failure is MediaPreProcessor.Failure) { - StringR.string.screen_media_upload_preview_error_failed_processing - } else { - StringR.string.screen_media_upload_preview_error_failed_sending - } - snackbarDispatcher.post(SnackbarMessage(snackbarMessage)) - } - - } } diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/Result.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/Result.kt index baa3b35e2b..3ddd4f9105 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/Result.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/Result.kt @@ -25,3 +25,16 @@ inline fun Result.mapFailure(transform: (exception: Throwable) -> else -> Result.failure(transform(exception)) } } + +/** + * Can be used to transform some Throwable into some other. + */ +inline fun Result.flatMap(transform: (R) -> Result): Result { + return when (val exception = exceptionOrNull()) { + null -> mapCatching(transform).fold( + onSuccess = { it }, + onFailure = { Result.failure(it) } + ) + else -> Result.failure(exception) + } +} diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt index cd45482871..2144f7bdd7 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt @@ -20,13 +20,13 @@ import android.net.Uri interface MediaPreProcessor { /** - * Given a [uri] and [mediaType], pre-processes the media before it's uploaded, resizing, transcoding, and removing sensitive info from its metadata. + * Given a [uri] and [mimeType], pre-processes the media before it's uploaded, resizing, transcoding, and removing sensitive info from its metadata. * If [deleteOriginal] is `true`, the file reference by the [uri] will be automatically deleted too when this process finishes. * @return a [Result] with the [MediaUploadInfo] containing all the info needed to begin the upload. */ suspend fun process( uri: Uri, - mediaType: MediaType, + mimeType: String, deleteOriginal: Boolean = false ): Result diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/MediaPreProcessorImpl.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt similarity index 90% rename from libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/MediaPreProcessorImpl.kt rename to libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt index 799191251c..3a500abbe6 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/MediaPreProcessorImpl.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt @@ -27,7 +27,9 @@ import io.element.android.libraries.androidutils.media.runAndRelease import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage +import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.media.AudioInfo @@ -37,7 +39,6 @@ import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.ThumbnailInfo import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.mediaupload.api.MediaPreProcessor -import io.element.android.libraries.mediaupload.api.MediaType import io.element.android.libraries.mediaupload.api.MediaUploadInfo import io.element.android.libraries.mediaupload.api.ThumbnailProcessingInfo import kotlinx.coroutines.Dispatchers @@ -53,7 +54,7 @@ import javax.inject.Inject import kotlin.time.Duration.Companion.seconds @ContributesBinding(AppScope::class) -class MediaPreProcessorImpl @Inject constructor( +class AndroidMediaPreProcessor @Inject constructor( @ApplicationContext private val context: Context, private val imageCompressor: ImageCompressor, private val videoCompressor: VideoCompressor, @@ -89,27 +90,18 @@ class MediaPreProcessorImpl @Inject constructor( override suspend fun process( uri: Uri, - mediaType: MediaType, + mimeType: String, deleteOriginal: Boolean, ): Result = runCatching { - // Camera returns an 'octet-stream' mimetype, so it needs to be overridden - val mimeType = contentResolver.getType(uri) - val mimeTypeOrDefault = if (mimeType == MimeTypes.OctetStream) { - when (mediaType) { - MediaType.Image -> MimeTypes.Jpeg - MediaType.Video -> MimeTypes.Mp4 - MediaType.Audio -> MimeTypes.Ogg - else -> mimeType - } - } else { - mimeType - } - val compressBeforeSending = mediaType in sequenceOf(MediaType.Image, MediaType.Video) - val result = if (compressBeforeSending && mimeType != MimeTypes.Gif) { - when (mediaType) { - MediaType.Image -> processImage(uri) - MediaType.Video -> processVideo(uri, mimeTypeOrDefault) - MediaType.Audio -> processAudio(uri, mimeTypeOrDefault) + val compressBeforeSending = ( + mimeType.isMimeTypeImage() && mimeType != MimeTypes.Gif) || + mimeType.isMimeTypeVideo() + + val result = if (compressBeforeSending) { + when { + mimeType.isMimeTypeImage() -> processImage(uri) + mimeType.isMimeTypeVideo() -> processVideo(uri, mimeType) + mimeType.isMimeTypeAudio() -> processAudio(uri, mimeType) else -> error("Cannot compress file of type: $mimeType") } } else { diff --git a/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt b/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt index e96bc08fd1..f8b7237bbe 100644 --- a/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt +++ b/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt @@ -19,7 +19,6 @@ package io.element.android.libraries.mediaupload.test import android.net.Uri import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.mediaupload.api.MediaPreProcessor -import io.element.android.libraries.mediaupload.api.MediaType import io.element.android.libraries.mediaupload.api.MediaUploadInfo import java.io.File @@ -37,7 +36,7 @@ class FakeMediaPreProcessor : MediaPreProcessor { ) ) - override suspend fun process(uri: Uri, mediaType: MediaType, deleteOriginal: Boolean): Result = result + override suspend fun process(uri: Uri, mimeType: String, deleteOriginal: Boolean): Result = result fun givenResult(value: Result) { this.result = value