From e0106fe90740f79c687d1d12f448a8ba0fc66463 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 25 May 2023 22:54:09 +0200 Subject: [PATCH] Media: some more improvements over MediaViewer --- .../messages/impl/MessagesFlowNode.kt | 29 +++-- .../messages/impl/media/local/LocalMedia.kt | 2 + .../impl/media/local/LocalMediaView.kt | 50 +++++-- .../impl/media/viewer/MediaViewerEvents.kt | 1 + .../impl/media/viewer/MediaViewerNode.kt | 1 + .../impl/media/viewer/MediaViewerPresenter.kt | 3 + .../impl/media/viewer/MediaViewerState.kt | 5 +- .../media/viewer/MediaViewerStateProvider.kt | 14 +- .../impl/media/viewer/MediaViewerView.kt | 122 ++++++++++++++---- .../components/event/TimelineItemVideoView.kt | 13 +- .../TimelineItemContentMessageFactory.kt | 8 +- .../event/TimelineItemImageContentProvider.kt | 6 +- .../model/event/TimelineItemVideoContent.kt | 2 +- .../event/TimelineItemVideoContentProvider.kt | 2 +- .../modifiers/RoundedBackground.kt | 40 ++++++ .../android/libraries/matrix/test/TestData.kt | 2 - 16 files changed, 226 insertions(+), 74 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/RoundedBackground.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index c8e35ccbe8..1e10a553f1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -26,7 +26,6 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push -import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackFader import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode @@ -41,8 +40,8 @@ import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.media.MediaSource -import kotlinx.android.parcel.Parcelize import kotlinx.collections.immutable.ImmutableList +import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) class MessagesFlowNode @AssistedInject constructor( @@ -65,7 +64,8 @@ class MessagesFlowNode @AssistedInject constructor( data class MediaViewer( val title: String, val mediaSource: MediaSource, - val mimeType: String? + val thumbnailSource: MediaSource?, + val mimeType: String?, ) : NavTarget @Parcelize @@ -93,7 +93,12 @@ class MessagesFlowNode @AssistedInject constructor( createNode(buildContext, listOf(callback)) } is NavTarget.MediaViewer -> { - val inputs = MediaViewerNode.Inputs(navTarget.title, navTarget.mediaSource, navTarget.mimeType) + val inputs = MediaViewerNode.Inputs( + name = navTarget.title, + mediaSource = navTarget.mediaSource, + thumbnailSource = navTarget.thumbnailSource, + mimeType = navTarget.mimeType, + ) createNode(buildContext, listOf(inputs)) } is NavTarget.AttachmentPreview -> { @@ -106,13 +111,22 @@ class MessagesFlowNode @AssistedInject constructor( private fun processEventClicked(event: TimelineItem.Event) { when (event.content) { is TimelineItemImageContent -> { - val mediaSource = event.content.mediaSource - val navTarget = NavTarget.MediaViewer(event.content.body, mediaSource, event.content.mimeType) + val navTarget = NavTarget.MediaViewer( + title = event.content.body, + mediaSource = event.content.mediaSource, + thumbnailSource = event.content.mediaSource, + mimeType = event.content.mimeType + ) backstack.push(navTarget) } is TimelineItemVideoContent -> { val mediaSource = event.content.videoSource - val navTarget = NavTarget.MediaViewer(event.content.body, mediaSource, event.content.mimeType) + val navTarget = NavTarget.MediaViewer( + title = event.content.body, + mediaSource = mediaSource, + thumbnailSource = event.content.thumbnailSource, + mimeType = event.content.mimeType, + ) backstack.push(navTarget) } else -> Unit @@ -124,7 +138,6 @@ class MessagesFlowNode @AssistedInject constructor( Children( navModel = backstack, modifier = modifier, - transitionHandler = rememberBackstackFader() ) } } 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 41ff1025ff..8305c8eee7 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 @@ -18,10 +18,12 @@ package io.element.android.features.messages.impl.media.local import android.net.Uri import android.os.Parcelable +import androidx.compose.runtime.Immutable import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize @Parcelize +@Immutable data class LocalMedia( val uri: Uri, val mimeType: String, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt index 1f3a67a8ec..8c94822947 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.Lifecycle import androidx.media3.common.MediaItem import androidx.media3.common.MimeTypes +import androidx.media3.common.Player import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer import androidx.media3.ui.AspectRatioFrameLayout @@ -47,16 +48,20 @@ import me.saket.telephoto.zoomable.rememberZoomableState @SuppressLint("UnsafeOptInUsageError") @Composable fun LocalMediaView( - localMedia: LocalMedia, - modifier: Modifier = Modifier + localMedia: LocalMedia?, + modifier: Modifier = Modifier, + mimeType: String? = localMedia?.mimeType, + onReady: () -> Unit = {}, ) { when { - MimeTypes.isImage(localMedia.mimeType) -> MediaImageView( + MimeTypes.isImage(mimeType) -> MediaImageView( localMedia = localMedia, + onReady = onReady, modifier = modifier ) - MimeTypes.isVideo(localMedia.mimeType) -> MediaVideoView( + MimeTypes.isVideo(mimeType) -> MediaVideoView( localMedia = localMedia, + onReady = onReady, modifier = modifier ) else -> Unit @@ -65,7 +70,8 @@ fun LocalMediaView( @Composable private fun MediaImageView( - localMedia: LocalMedia, + localMedia: LocalMedia?, + onReady: () -> Unit, modifier: Modifier = Modifier, ) { if (LocalInspectionMode.current) { @@ -78,10 +84,16 @@ private fun MediaImageView( val zoomableState = rememberZoomableState( zoomSpec = ZoomSpec(maxZoomFactor = 3f) ) + val zoomableImageState = rememberZoomableImageState(zoomableState) + LaunchedEffect(zoomableImageState.isImageDisplayed) { + if (zoomableImageState.isImageDisplayed) { + onReady() + } + } ZoomableAsyncImage( modifier = modifier.fillMaxSize(), - state = rememberZoomableImageState(zoomableState), - model = localMedia.model, + state = zoomableImageState, + model = localMedia?.model, contentDescription = "Image", contentScale = ContentScale.Fit, ) @@ -91,22 +103,32 @@ private fun MediaImageView( @UnstableApi @Composable fun MediaVideoView( - localMedia: LocalMedia, + localMedia: LocalMedia?, + onReady: () -> Unit, modifier: Modifier = Modifier, ) { val context = LocalContext.current + val playerListener = object : Player.Listener { + override fun onRenderedFirstFrame() { + onReady() + } + } val exoPlayer = remember { - ExoPlayer.Builder(context).build() + ExoPlayer.Builder(context) + .build() .apply { - this.playWhenReady = true + addListener(playerListener) this.prepare() } } - LaunchedEffect(localMedia.uri) { - val mediaItem = MediaItem.fromUri(localMedia.uri) - exoPlayer.setMediaItem(mediaItem) + if (localMedia?.uri != null) { + LaunchedEffect(localMedia.uri) { + val mediaItem = MediaItem.fromUri(localMedia.uri) + exoPlayer.setMediaItem(mediaItem) + } + } else { + exoPlayer.setMediaItems(emptyList()) } - AndroidView( factory = { PlayerView(context).apply { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerEvents.kt index 4b60c05b3d..b0bbad5ec2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerEvents.kt @@ -18,4 +18,5 @@ package io.element.android.features.messages.impl.media.viewer sealed interface MediaViewerEvents { object RetryLoading : MediaViewerEvents + object ClearLoadingError : MediaViewerEvents } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerNode.kt index 7321eff416..247a86263f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerNode.kt @@ -40,6 +40,7 @@ class MediaViewerNode @AssistedInject constructor( data class Inputs( val name: String, val mediaSource: MediaSource, + val thumbnailSource: MediaSource?, val mimeType: String? ) : NodeInputs diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt index 542436ed9f..01b562f62c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt @@ -67,11 +67,14 @@ class MediaViewerPresenter @AssistedInject constructor( fun handleEvents(mediaViewerEvents: MediaViewerEvents) { when (mediaViewerEvents) { MediaViewerEvents.RetryLoading -> loadMediaTrigger++ + MediaViewerEvents.ClearLoadingError -> localMedia.value = Async.Uninitialized } } return MediaViewerState( name = inputs.name, + mimeType = inputs.mimeType, + thumbnailSource = inputs.thumbnailSource, downloadedMedia = localMedia.value, eventSink = ::handleEvents ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt index b5279af6c8..c42263dd3e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt @@ -18,9 +18,12 @@ package io.element.android.features.messages.impl.media.viewer import io.element.android.features.messages.impl.media.local.LocalMedia import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.media.MediaSource data class MediaViewerState( val name: String, + val mimeType: String?, + val thumbnailSource: MediaSource?, val downloadedMedia: Async, - val eventSink: (MediaViewerEvents) -> Unit + val eventSink: (MediaViewerEvents) -> Unit, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt index 45ac530b8e..6c54eb3b05 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt @@ -31,7 +31,14 @@ open class MediaViewerStateProvider : PreviewParameterProvider aMediaViewerState( Async.Success( LocalMedia( - Uri.EMPTY, MimeTypes.IMAGE_JPEG, "a file", 100L + Uri.EMPTY, MimeTypes.IMAGE_JPEG, "an image file", 100L + ) + ), + ), + aMediaViewerState( + Async.Success( + LocalMedia( + Uri.EMPTY, MimeTypes.VIDEO_MP4, "a video file", 100L ) ), ) @@ -40,6 +47,7 @@ open class MediaViewerStateProvider : PreviewParameterProvider fun aMediaViewerState(downloadedMedia: Async = Async.Uninitialized) = MediaViewerState( name = "A media", + mimeType = MimeTypes.IMAGE_JPEG, + thumbnailSource = null, downloadedMedia = downloadedMedia, - eventSink = {} -) +) {} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt index b5ef22b596..b2957cac18 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt @@ -14,33 +14,36 @@ * limitations under the License. */ -@file:OptIn(ExperimentalMaterial3Api::class) package io.element.android.features.messages.impl.media.viewer import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale 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 coil.compose.AsyncImage import io.element.android.features.messages.impl.media.local.LocalMediaView import io.element.android.libraries.architecture.Async +import io.element.android.libraries.architecture.isLoading +import io.element.android.libraries.designsystem.components.dialogs.RetryDialog +import io.element.android.libraries.designsystem.modifiers.roundedBackground import io.element.android.libraries.designsystem.preview.ElementPreviewDark -import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Scaffold -import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.ui.media.MediaRequestData +import kotlinx.coroutines.delay import io.element.android.libraries.ui.strings.R as StringR @Composable @@ -53,6 +56,32 @@ fun MediaViewerView( state.eventSink(MediaViewerEvents.RetryLoading) } + fun onDismissError() { + state.eventSink(MediaViewerEvents.ClearLoadingError) + } + + var showProgress by remember { + mutableStateOf(false) + } + + // Trick to avoid showing progress indicator if the media is already on disk. + // When sdk will expose download progress we'll be able to remove this. + LaunchedEffect(state.downloadedMedia) { + showProgress = false + delay(100) + if (state.downloadedMedia.isLoading()) { + showProgress = true + } + } + + var showThumbnail by remember { + mutableStateOf(true) + } + + fun onMediaReady() { + showThumbnail = false + } + Scaffold(modifier) { Box( modifier = Modifier @@ -60,10 +89,55 @@ fun MediaViewerView( .padding(it), contentAlignment = Alignment.Center ) { - when (state.downloadedMedia) { - is Async.Success -> LocalMediaView(state.downloadedMedia.state) - is Async.Failure -> ErrorView(stringResource(id = StringR.string.error_unknown), ::onRetry) - else -> CircularProgressIndicator() + if (state.downloadedMedia is Async.Failure) { + ErrorView( + errorMessage = stringResource(id = StringR.string.error_unknown), + onRetry = ::onRetry, + onDismiss = ::onDismissError + ) + } + LocalMediaView( + localMedia = state.downloadedMedia.dataOrNull(), + mimeType = state.mimeType, + onReady = ::onMediaReady + ) + ThumbnailView( + thumbnailSource = state.thumbnailSource, + showThumbnail = showThumbnail, + showProgress = showProgress, + ) + } + } +} + +@Composable +private fun ThumbnailView( + thumbnailSource: MediaSource?, + showThumbnail: Boolean, + showProgress: Boolean, +) { + if (!showThumbnail) return + + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + val mediaRequestData = MediaRequestData( + source = thumbnailSource, + kind = MediaRequestData.Kind.Content + ) + AsyncImage( + modifier = Modifier.fillMaxSize(), + model = mediaRequestData, + contentScale = ContentScale.Fit, + contentDescription = null, + ) + if (showProgress) { + Box( + modifier = Modifier.roundedBackground(), + contentAlignment = Alignment.Center, + ) { + CircularProgressIndicator() } } } @@ -73,21 +147,15 @@ fun MediaViewerView( private fun ErrorView( errorMessage: String, onRetry: () -> Unit, + onDismiss: () -> Unit, modifier: Modifier = Modifier, ) { - Column( - modifier = modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text(text = errorMessage) - Spacer(modifier = Modifier.size(16.dp)) - Button( - onClick = onRetry - ) { - Text(text = stringResource(id = StringR.string.action_retry)) - } - - } + RetryDialog( + modifier = modifier, + content = errorMessage, + onRetry = onRetry, + onDismiss = onDismiss + ) } @Preview diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt index 84f27720d1..883f7b30f3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt @@ -17,26 +17,22 @@ package io.element.android.features.messages.impl.timeline.components.event import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.layout.ContentScale 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.timeline.components.blurhash.BlurHashAsyncImage import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContentProvider +import io.element.android.libraries.designsystem.modifiers.roundedBackground import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.matrix.ui.media.MediaRequestData @@ -54,15 +50,12 @@ fun TimelineItemVideoView( ) { BlurHashAsyncImage( model = MediaRequestData(content.thumbnailSource, MediaRequestData.Kind.Content), - blurHash = content.blurhash, + blurHash = content.blurHash, modifier = Modifier.fillMaxSize(), contentScale = ContentScale.Fit, ) Box( - modifier = Modifier - .size(50.dp) - .clip(CircleShape) - .background(color = Color.Black.copy(alpha = 0.5f)), + modifier = Modifier.roundedBackground(), contentAlignment = Alignment.Center, ) { Image( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index 83163741c5..db68201f9c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -46,11 +46,11 @@ class TimelineItemContentMessageFactory @Inject constructor() { val aspectRatio = aspectRatioOf(messageType.info?.width, messageType.info?.height) TimelineItemImageContent( body = messageType.body, - height = messageType.info?.height?.toInt(), - width = messageType.info?.width?.toInt(), - mimeType = messageType.info?.mimetype, mediaSource = messageType.source, + mimeType = messageType.info?.mimetype, blurhash = messageType.info?.blurhash, + width = messageType.info?.width?.toInt(), + height = messageType.info?.height?.toInt(), aspectRatio = aspectRatio ) } @@ -64,7 +64,7 @@ class TimelineItemContentMessageFactory @Inject constructor() { width = messageType.info?.width?.toInt(), height = messageType.info?.height?.toInt(), duration = messageType.info?.duration ?: 0L, - blurhash = messageType.info?.blurhash, + blurHash = messageType.info?.blurhash, aspectRatio = aspectRatio ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt index 6324789d8f..97bbf6ed41 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt @@ -32,9 +32,9 @@ open class TimelineItemImageContentProvider : PreviewParameterProvider