mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-10-31 16:22:49 +00:00
Merge pull request #7901 from element-hq/feature/bma/mainThreadBlocking
Avoid blocking the main thread in several places
This commit is contained in:
+51
-38
@@ -43,6 +43,7 @@ import io.element.android.features.messages.impl.timeline.TimelineController
|
||||
import io.element.android.features.messages.impl.utils.TextPillificationHelper
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
@@ -110,6 +111,7 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes.Any as AnyMimeTypes
|
||||
@@ -145,6 +147,7 @@ class MessageComposerPresenter(
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
private val contentScannerService: ContentScannerService,
|
||||
private val contentValidationCache: EventContentValidationCache,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
) : Presenter<MessageComposerState> {
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
@@ -190,22 +193,22 @@ class MessageComposerPresenter(
|
||||
.collectAsState(initial = false)
|
||||
|
||||
val galleryMediaPicker = mediaPickerProvider.registerGalleryPicker { uri, mimeType ->
|
||||
handlePickedMedia(uri, mimeType)
|
||||
localCoroutineScope.handlePickedMedia(uri, mimeType)
|
||||
}
|
||||
val galleryMultiMediaPicker = mediaPickerProvider.registerGalleryMultiPicker { uris ->
|
||||
handlePickedMediaList(uris)
|
||||
localCoroutineScope.handlePickedMediaList(uris)
|
||||
}
|
||||
val filesPicker = mediaPickerProvider.registerFileMultiPicker(AnyMimeTypes) { uris ->
|
||||
handlePickedMediaList(uris, sendAsFile = true)
|
||||
localCoroutineScope.handlePickedMediaList(uris, sendAsFile = true)
|
||||
}
|
||||
val fileSinglePicker = mediaPickerProvider.registerFilePicker(AnyMimeTypes) { uri, mimeType ->
|
||||
handlePickedMedia(uri, mimeType ?: MimeTypes.OctetStream, sendAsFile = true)
|
||||
localCoroutineScope.handlePickedMedia(uri, mimeType ?: MimeTypes.OctetStream, sendAsFile = true)
|
||||
}
|
||||
val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker { uri ->
|
||||
handlePickedMedia(uri, MimeTypes.Jpeg)
|
||||
localCoroutineScope.handlePickedMedia(uri, MimeTypes.Jpeg)
|
||||
}
|
||||
val cameraVideoPicker = mediaPickerProvider.registerCameraVideoPicker { uri ->
|
||||
handlePickedMedia(uri, MimeTypes.Mp4)
|
||||
localCoroutineScope.handlePickedMedia(uri, MimeTypes.Mp4)
|
||||
}
|
||||
val isFullScreen = rememberSaveable {
|
||||
mutableStateOf(false)
|
||||
@@ -283,17 +286,16 @@ class MessageComposerPresenter(
|
||||
}
|
||||
is MessageComposerEvent.SendUri -> {
|
||||
val inReplyToEventId = (messageComposerContext.composerMode as? MessageComposerMode.Reply)?.eventId
|
||||
sessionCoroutineScope.sendAttachment(
|
||||
attachment = Attachment.Media(
|
||||
localMedia = localMediaFactory.createFromUri(
|
||||
uri = event.uri,
|
||||
mimeType = null,
|
||||
name = null,
|
||||
formattedFileSize = null
|
||||
),
|
||||
),
|
||||
inReplyToEventId = inReplyToEventId,
|
||||
)
|
||||
sessionCoroutineScope.launch {
|
||||
val localMedia = createLocalMedia(
|
||||
uri = event.uri,
|
||||
mimeType = null,
|
||||
)
|
||||
sendAttachment(
|
||||
attachment = Attachment.Media(localMedia),
|
||||
inReplyToEventId = inReplyToEventId,
|
||||
)
|
||||
}
|
||||
|
||||
resetComposerModeAfterAttaching()
|
||||
}
|
||||
@@ -637,17 +639,28 @@ class MessageComposerPresenter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePickedMedia(
|
||||
uri: Uri?,
|
||||
mimeType: String? = null,
|
||||
sendAsFile: Boolean = false,
|
||||
) {
|
||||
uri ?: return
|
||||
val localMedia = localMediaFactory.createFromUri(
|
||||
// Creating the LocalMedia queries the ContentResolver and can decode the media, so do it off the main thread
|
||||
private suspend fun createLocalMedia(
|
||||
uri: Uri,
|
||||
mimeType: String?,
|
||||
) = withContext(dispatchers.io) {
|
||||
localMediaFactory.createFromUri(
|
||||
uri = uri,
|
||||
mimeType = mimeType,
|
||||
name = null,
|
||||
formattedFileSize = null
|
||||
formattedFileSize = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.handlePickedMedia(
|
||||
uri: Uri?,
|
||||
mimeType: String? = null,
|
||||
sendAsFile: Boolean = false,
|
||||
) = launch {
|
||||
uri ?: return@launch
|
||||
val localMedia = createLocalMedia(
|
||||
uri = uri,
|
||||
mimeType = mimeType,
|
||||
)
|
||||
val mediaAttachment = Attachment.Media(localMedia, sendAsFile = sendAsFile)
|
||||
val inReplyToEventId = (messageComposerContext.composerMode as? MessageComposerMode.Reply)?.eventId
|
||||
@@ -656,7 +669,7 @@ class MessageComposerPresenter(
|
||||
resetComposerModeAfterAttaching()
|
||||
}
|
||||
|
||||
private fun handlePickedMediaList(
|
||||
private fun CoroutineScope.handlePickedMediaList(
|
||||
uris: List<Uri>,
|
||||
sendAsFile: Boolean = false,
|
||||
) {
|
||||
@@ -665,19 +678,19 @@ class MessageComposerPresenter(
|
||||
handlePickedMedia(uris.first(), sendAsFile = sendAsFile)
|
||||
return
|
||||
}
|
||||
val attachments = uris.map { uri ->
|
||||
val localMedia = localMediaFactory.createFromUri(
|
||||
uri = uri,
|
||||
mimeType = null,
|
||||
name = null,
|
||||
formattedFileSize = null,
|
||||
)
|
||||
Attachment.Media(localMedia, sendAsFile = sendAsFile)
|
||||
}.toImmutableList()
|
||||
val inReplyToEventId = (messageComposerContext.composerMode as? MessageComposerMode.Reply)?.eventId
|
||||
navigator.navigateToPreviewAttachments(attachments, inReplyToEventId)
|
||||
launch {
|
||||
val attachments = uris.map { uri ->
|
||||
val localMedia = createLocalMedia(
|
||||
uri = uri,
|
||||
mimeType = null,
|
||||
)
|
||||
Attachment.Media(localMedia, sendAsFile = sendAsFile)
|
||||
}.toImmutableList()
|
||||
val inReplyToEventId = (messageComposerContext.composerMode as? MessageComposerMode.Reply)?.eventId
|
||||
navigator.navigateToPreviewAttachments(attachments, inReplyToEventId)
|
||||
|
||||
resetComposerModeAfterAttaching()
|
||||
resetComposerModeAfterAttaching()
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetComposerModeAfterAttaching() {
|
||||
|
||||
+1
@@ -324,6 +324,7 @@ class MessageComposerPresenterSlashCommandTest {
|
||||
featureFlagService = featureFlagService,
|
||||
contentScannerService = { _, _ -> },
|
||||
contentValidationCache = InMemoryEventContentValidationCache(),
|
||||
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||
).apply {
|
||||
isTesting = true
|
||||
showTextFormatting = isRichTextEditorEnabled
|
||||
|
||||
+1
@@ -1723,6 +1723,7 @@ class MessageComposerPresenterTest : RobolectricTest() {
|
||||
featureFlagService = featureFlagService,
|
||||
contentScannerService = { _, _ -> },
|
||||
contentValidationCache = InMemoryEventContentValidationCache(),
|
||||
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||
).apply {
|
||||
isTesting = true
|
||||
showTextFormatting = isRichTextEditorEnabled
|
||||
|
||||
Reference in New Issue
Block a user