Media: improve media viewer
This commit is contained in:
@@ -62,7 +62,11 @@ class MessagesFlowNode @AssistedInject constructor(
|
||||
object Messages : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class MediaViewer(val title: String, val mediaSource: MatrixMediaSource) : NavTarget
|
||||
data class MediaViewer(
|
||||
val title: String,
|
||||
val mediaSource: MatrixMediaSource,
|
||||
val mimeType: String?
|
||||
) : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class AttachmentPreview(val attachment: Attachment) : NavTarget
|
||||
@@ -89,7 +93,7 @@ class MessagesFlowNode @AssistedInject constructor(
|
||||
createNode<MessagesNode>(buildContext, listOf(callback))
|
||||
}
|
||||
is NavTarget.MediaViewer -> {
|
||||
val inputs = MediaViewerNode.Inputs(navTarget.title, navTarget.mediaSource)
|
||||
val inputs = MediaViewerNode.Inputs(navTarget.title, navTarget.mediaSource, navTarget.mimeType)
|
||||
createNode<MediaViewerNode>(buildContext, listOf(inputs))
|
||||
}
|
||||
is NavTarget.AttachmentPreview -> {
|
||||
@@ -103,12 +107,12 @@ class MessagesFlowNode @AssistedInject constructor(
|
||||
when (event.content) {
|
||||
is TimelineItemImageContent -> {
|
||||
val mediaSource = event.content.mediaSource
|
||||
val navTarget = NavTarget.MediaViewer(event.content.body, mediaSource)
|
||||
val navTarget = NavTarget.MediaViewer(event.content.body, mediaSource, event.content.mimeType)
|
||||
backstack.push(navTarget)
|
||||
}
|
||||
is TimelineItemVideoContent -> {
|
||||
val mediaSource = event.content.videoSource
|
||||
val navTarget = NavTarget.MediaViewer(event.content.body, mediaSource)
|
||||
val navTarget = NavTarget.MediaViewer(event.content.body, mediaSource, event.content.mimeType)
|
||||
backstack.push(navTarget)
|
||||
}
|
||||
else -> Unit
|
||||
|
||||
@@ -18,7 +18,7 @@ package io.element.android.features.messages.impl.media.local
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class LocalMedia(
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.widget.FrameLayout
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
@@ -84,13 +86,17 @@ fun MediaVideoView(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val exoPlayer = ExoPlayer.Builder(LocalContext.current).build()
|
||||
val mediaItem = MediaItem.Builder()
|
||||
.setUri(uri)
|
||||
.build()
|
||||
exoPlayer.playWhenReady
|
||||
exoPlayer.setMediaItem(mediaItem)
|
||||
exoPlayer.prepare()
|
||||
val exoPlayer = remember {
|
||||
ExoPlayer.Builder(context).build()
|
||||
.apply {
|
||||
this.playWhenReady = true
|
||||
this.prepare()
|
||||
}
|
||||
}
|
||||
LaunchedEffect(uri) {
|
||||
val mediaItem = MediaItem.fromUri(uri)
|
||||
exoPlayer.setMediaItem(mediaItem)
|
||||
}
|
||||
|
||||
AndroidView(
|
||||
factory = {
|
||||
@@ -98,8 +104,10 @@ fun MediaVideoView(
|
||||
player = exoPlayer
|
||||
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||
layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
||||
controllerShowTimeoutMs = 3000
|
||||
}
|
||||
}, modifier = modifier.fillMaxSize()
|
||||
},
|
||||
modifier = modifier.fillMaxSize()
|
||||
)
|
||||
|
||||
OnLifecycleEvent { _, event ->
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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.media.viewer
|
||||
|
||||
sealed interface MediaViewerEvents {
|
||||
object RetryLoading : MediaViewerEvents
|
||||
object SaveOnDisk : MediaViewerEvents
|
||||
}
|
||||
@@ -39,11 +39,12 @@ class MediaViewerNode @AssistedInject constructor(
|
||||
data class Inputs(
|
||||
val name: String,
|
||||
val mediaSource: MatrixMediaSource,
|
||||
val mimeType: String?
|
||||
) : NodeInputs
|
||||
|
||||
private val inputs: Inputs = inputs()
|
||||
|
||||
private val presenter = presenterFactory.create(inputs.name, inputs.mediaSource)
|
||||
private val presenter = presenterFactory.create(inputs)
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
|
||||
@@ -17,8 +17,14 @@
|
||||
package io.element.android.features.messages.impl.media.viewer
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.core.net.toUri
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
@@ -27,37 +33,65 @@ import io.element.android.features.messages.impl.media.local.LocalMediaFactory
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MediaViewerPresenter @AssistedInject constructor(
|
||||
@Assisted private val name: String,
|
||||
@Assisted private val mediaSource: MatrixMediaSource,
|
||||
@Assisted private val inputs: MediaViewerNode.Inputs,
|
||||
private val localMediaFactory: LocalMediaFactory,
|
||||
private val client: MatrixClient,
|
||||
) : Presenter<MediaViewerState> {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(name: String, mediaSource: MatrixMediaSource): MediaViewerPresenter
|
||||
fun create(inputs: MediaViewerNode.Inputs): MediaViewerPresenter
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun present(): MediaViewerState {
|
||||
val localMedia by produceState<Async<LocalMedia>>(initialValue = Async.Uninitialized) {
|
||||
value = Async.Loading(null)
|
||||
//TODO we are missing some permissions to use this API
|
||||
client.mediaLoader.loadMediaFile(mediaSource, null)
|
||||
.onSuccess {
|
||||
val localMedia = localMediaFactory.createFromUri(uri = it, null)
|
||||
Async.Success(localMedia)
|
||||
}.onFailure {
|
||||
Async.Failure(it, null)
|
||||
}
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
var loadMediaTrigger by remember { mutableStateOf(0) }
|
||||
val mediaFile: MutableState<MediaFile?> = remember {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
val localMedia: MutableState<Async<LocalMedia>> = remember {
|
||||
mutableStateOf(Async.Uninitialized)
|
||||
}
|
||||
DisposableEffect(loadMediaTrigger) {
|
||||
coroutineScope.loadMedia(mediaFile, localMedia)
|
||||
onDispose {
|
||||
mediaFile.value?.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun handleEvents(mediaViewerEvents: MediaViewerEvents) {
|
||||
when (mediaViewerEvents) {
|
||||
MediaViewerEvents.RetryLoading -> loadMediaTrigger++
|
||||
MediaViewerEvents.SaveOnDisk -> TODO()
|
||||
}
|
||||
}
|
||||
|
||||
return MediaViewerState(
|
||||
name = name,
|
||||
downloadedMedia = localMedia,
|
||||
name = inputs.name,
|
||||
downloadedMedia = localMedia.value,
|
||||
eventSink = ::handleEvents
|
||||
)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.loadMedia(mediaFile: MutableState<MediaFile?>, localMedia: MutableState<Async<LocalMedia>>) = launch {
|
||||
mediaFile.value = null
|
||||
localMedia.value = Async.Loading()
|
||||
client.mediaLoader.loadMediaFile(inputs.mediaSource, inputs.mimeType)
|
||||
.onSuccess {
|
||||
mediaFile.value = it
|
||||
}.mapCatching {
|
||||
val uri = it.path().toUri()
|
||||
localMediaFactory.createFromUri(uri, inputs.mimeType)!!
|
||||
}.onSuccess {
|
||||
localMedia.value = Async.Success(it)
|
||||
}.onFailure {
|
||||
localMedia.value = Async.Failure(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,5 +21,6 @@ import io.element.android.libraries.architecture.Async
|
||||
|
||||
data class MediaViewerState(
|
||||
val name: String,
|
||||
val downloadedMedia: Async<LocalMedia>
|
||||
val downloadedMedia: Async<LocalMedia>,
|
||||
val eventSink: (MediaViewerEvents) -> Unit
|
||||
)
|
||||
|
||||
@@ -29,5 +29,6 @@ open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState>
|
||||
|
||||
fun aMediaViewerState() = MediaViewerState(
|
||||
name = "A media",
|
||||
downloadedMedia = Async.Uninitialized
|
||||
downloadedMedia = Async.Uninitialized,
|
||||
eventSink = {}
|
||||
)
|
||||
|
||||
@@ -19,37 +19,50 @@
|
||||
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.ui.Alignment
|
||||
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.media.local.LocalMediaView
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||
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.ui.strings.R.string as StringR
|
||||
|
||||
@Composable
|
||||
fun MediaViewerView(
|
||||
state: MediaViewerState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
fun onRetry() {
|
||||
state.eventSink(MediaViewerEvents.RetryLoading)
|
||||
}
|
||||
|
||||
Scaffold(modifier) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(it),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
when (state.downloadedMedia) {
|
||||
is Async.Success -> LocalMediaView(state.downloadedMedia.state)
|
||||
is Async.Failure -> ErrorDialog(
|
||||
content = "Error while downloading the media",
|
||||
)
|
||||
is Async.Failure -> ErrorView("Error while downloading", ::onRetry)
|
||||
else -> CircularProgressIndicator(
|
||||
strokeWidth = 2.dp,
|
||||
)
|
||||
@@ -58,6 +71,27 @@ fun MediaViewerView(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ErrorView(
|
||||
errorMessage: String,
|
||||
onRetry: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(text = errorMessage)
|
||||
Spacer(modifier = Modifier.size(8.dp))
|
||||
Button(
|
||||
onClick = onRetry
|
||||
) {
|
||||
Text(text = stringResource(id = StringR.action_retry))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun MediaViewerViewLightPreview(@PreviewParameter(MediaViewerStateProvider::class) state: MediaViewerState) =
|
||||
|
||||
@@ -52,6 +52,7 @@ class TimelineItemContentMessageFactory @Inject constructor() {
|
||||
body = messageType.body,
|
||||
height = messageType.info?.height?.toInt(),
|
||||
width = messageType.info?.width?.toInt(),
|
||||
mimeType = messageType.info?.mimetype,
|
||||
mediaSource = messageType.source,
|
||||
blurhash = messageType.info?.blurhash,
|
||||
aspectRatio = aspectRatio
|
||||
@@ -69,7 +70,7 @@ class TimelineItemContentMessageFactory @Inject constructor() {
|
||||
body = messageType.body,
|
||||
thumbnailSource = messageType.info?.thumbnailSource,
|
||||
videoSource = messageType.source,
|
||||
mimetype = messageType.info?.mimetype,
|
||||
mimeType = messageType.info?.mimetype,
|
||||
width = messageType.info?.width?.toInt(),
|
||||
height = messageType.info?.height?.toInt(),
|
||||
duration = messageType.info?.duration ?: 0L,
|
||||
|
||||
@@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.media.MatrixMediaSource
|
||||
data class TimelineItemImageContent(
|
||||
val body: String,
|
||||
val mediaSource: MatrixMediaSource,
|
||||
val mimeType: String?,
|
||||
val blurhash: String?,
|
||||
val width: Int?,
|
||||
val height: Int?,
|
||||
|
||||
@@ -34,6 +34,7 @@ fun aTimelineItemImageContent() = TimelineItemImageContent(
|
||||
mediaSource = MatrixMediaSource(""),
|
||||
blurhash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr",
|
||||
aspectRatio = 0.5f,
|
||||
mimeType = "null",
|
||||
height = null,
|
||||
width = null
|
||||
)
|
||||
|
||||
@@ -27,7 +27,7 @@ data class TimelineItemVideoContent(
|
||||
val blurhash: String?,
|
||||
val height: Int?,
|
||||
val width: Int?,
|
||||
val mimetype: String?,
|
||||
val mimeType: String?,
|
||||
) : TimelineItemEventContent {
|
||||
override val type: String = "TimelineItemImageContent"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package io.element.android.features.messages.impl.timeline.model.event
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
|
||||
import io.element.android.libraries.matrix.ui.media.MediaRequestData
|
||||
|
||||
open class TimelineItemVideoContentProvider : PreviewParameterProvider<TimelineItemVideoContent> {
|
||||
override val values: Sequence<TimelineItemVideoContent>
|
||||
@@ -38,5 +37,5 @@ fun aTimelineItemVideoContent() = TimelineItemVideoContent(
|
||||
videoSource = MatrixMediaSource(""),
|
||||
height = null,
|
||||
width = null,
|
||||
mimetype = null
|
||||
mimeType = null
|
||||
)
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package io.element.android.libraries.matrix.api.media
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
interface MatrixMediaLoader {
|
||||
/**
|
||||
* @param url to fetch the content for.
|
||||
@@ -36,7 +34,7 @@ interface MatrixMediaLoader {
|
||||
/**
|
||||
* @param url to fetch the data for.
|
||||
* @param mimeType: optional mime type
|
||||
* @return a [Result] of [Uri]. It's the uri of the downloaded file.
|
||||
* @return a [Result] of [MediaFile]
|
||||
*/
|
||||
suspend fun loadMediaFile(source: MatrixMediaSource, mimeType: String?): Result<Uri>
|
||||
suspend fun loadMediaFile(source: MatrixMediaSource, mimeType: String?): Result<MediaFile>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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.libraries.matrix.api.media
|
||||
|
||||
import java.io.Closeable
|
||||
|
||||
/**
|
||||
* A wrapper around a media file on the disk.
|
||||
* When closed the file will be removed from the disk.
|
||||
*/
|
||||
interface MediaFile : Closeable {
|
||||
fun path(): String
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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.libraries.matrix.impl.media
|
||||
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
import org.matrix.rustcomponents.sdk.MediaFileHandle
|
||||
|
||||
class RustMediaFile(private val inner: MediaFileHandle) : MediaFile {
|
||||
|
||||
override fun path(): String {
|
||||
return inner.path()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
inner.close()
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,14 @@
|
||||
|
||||
package io.element.android.libraries.matrix.impl.media
|
||||
|
||||
import android.net.Uri
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.mediaSourceFromUrl
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import java.io.File
|
||||
|
||||
class RustMediaLoader(
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
@@ -59,19 +58,16 @@ class RustMediaLoader(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadMediaFile(source: MatrixMediaSource, mimeType: String?): Result<Uri> =
|
||||
override suspend fun loadMediaFile(source: MatrixMediaSource, mimeType: String?): Result<MediaFile> =
|
||||
withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
mediaSourceFromUrl(source.url).use { mediaSource ->
|
||||
innerClient.getMediaFile(
|
||||
val mediaFile = innerClient.getMediaFile(
|
||||
mediaSource = mediaSource,
|
||||
mimeType = mimeType ?: "application/octet-stream"
|
||||
).use {
|
||||
val file = File(it.path())
|
||||
Uri.fromFile(file)
|
||||
}
|
||||
)
|
||||
RustMediaFile(mediaFile)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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.libraries.matrix.test.media
|
||||
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
|
||||
class FakeMediaFile(private val path: String) : MediaFile {
|
||||
override fun path(): String {
|
||||
return path
|
||||
}
|
||||
|
||||
override fun close() = Unit
|
||||
}
|
||||
@@ -16,10 +16,9 @@
|
||||
|
||||
package io.element.android.libraries.matrix.test.media
|
||||
|
||||
import android.net.Uri
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
|
||||
import java.io.File
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
|
||||
class FakeMediaLoader : MatrixMediaLoader {
|
||||
|
||||
@@ -41,11 +40,11 @@ class FakeMediaLoader : MatrixMediaLoader {
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadMediaFile(source: MatrixMediaSource, mimeType: String?): Result<Uri> {
|
||||
override suspend fun loadMediaFile(source: MatrixMediaSource, mimeType: String?): Result<MediaFile> {
|
||||
return if (shouldFail) {
|
||||
Result.failure(RuntimeException())
|
||||
} else {
|
||||
return Result.success(Uri.fromFile(File("path")))
|
||||
return Result.success(FakeMediaFile(""))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user