Browse Source

Media: finally revert to using only uri but with the proper scheme..

feature/fga/small_timeline_improvements
ganfra 1 year ago
parent
commit
966199a007
  1. 9
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt
  2. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt
  3. 20
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaActionsHandler.kt
  4. 17
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt
  5. 19
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMedia.kt
  6. 11
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt
  7. 27
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/exoplayer/LocalMediaExt.kt
  8. 6
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt
  9. 11
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt
  10. 3
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt
  11. 5
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaFile.kt

9
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt

@ -25,7 +25,6 @@ import dagger.assisted.Assisted @@ -25,7 +25,6 @@ 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.features.messages.impl.media.local.LocalMedia
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.executeResult
@ -85,13 +84,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( @@ -85,13 +84,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
sendActionState: MutableState<Async<Unit>>,
) {
suspend {
when (mediaAttachment.localMedia.source) {
is LocalMedia.Source.FromUri -> {
mediaSender.sendMedia(mediaAttachment.localMedia.source.uri, mediaAttachment.localMedia.mimeType, mediaAttachment.compressIfPossible)
}
else -> error("Attachment should be defined by a uri")
}
mediaSender.sendMedia(mediaAttachment.localMedia.uri, mediaAttachment.localMedia.mimeType, mediaAttachment.compressIfPossible)
}.executeResult(sendActionState)
}
}

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt

@ -17,11 +17,11 @@ @@ -17,11 +17,11 @@
package io.element.android.features.messages.impl.attachments.preview
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
import java.io.File
open class AttachmentsPreviewStateProvider : PreviewParameterProvider<AttachmentsPreviewState> {
override val values: Sequence<AttachmentsPreviewState>
@ -34,7 +34,7 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider<Attachment @@ -34,7 +34,7 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider<Attachment
fun anAttachmentsPreviewState(sendActionState: Async<Unit> = Async.Uninitialized) = AttachmentsPreviewState(
attachment = Attachment.Media(
localMedia = LocalMedia(LocalMedia.Source.FromFile(File("path")), MimeTypes.Jpeg, "an image", 1000L),
localMedia = LocalMedia("path".toUri(), MimeTypes.Jpeg, "an image", 1000L),
compressIfPossible = true
),
sendActionState = sendActionState,

20
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaActionsHandler.kt

@ -16,10 +16,8 @@ @@ -16,10 +16,8 @@
package io.element.android.features.messages.impl.media.local
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
@ -62,10 +60,10 @@ class AndroidLocalMediaActionsHandler @Inject constructor( @@ -62,10 +60,10 @@ class AndroidLocalMediaActionsHandler @Inject constructor(
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
}
val resolver = context.contentResolver
val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
if (uri != null) {
localMedia.openStream(resolver)?.use { input ->
resolver.openOutputStream(uri).use { output ->
val outputUri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
if (outputUri != null) {
localMedia.openStream()?.use { input ->
resolver.openOutputStream(outputUri).use { output ->
input.copyTo(output!!, DEFAULT_BUFFER_SIZE)
}
}
@ -77,18 +75,14 @@ class AndroidLocalMediaActionsHandler @Inject constructor( @@ -77,18 +75,14 @@ class AndroidLocalMediaActionsHandler @Inject constructor(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
localMedia.name ?: ""
)
localMedia.openStream(context.contentResolver)?.use { input ->
localMedia.openStream()?.use { input ->
FileOutputStream(target).use { output ->
input.copyTo(output)
}
}
}
private fun LocalMedia.openStream(contentResolver: ContentResolver): InputStream? {
return when (val model = model) {
is File -> model.inputStream()
is Uri -> contentResolver.openInputStream(model)
else -> null
}
private fun LocalMedia.openStream(): InputStream? {
return context.contentResolver.openInputStream(uri)
}
}

17
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt

@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.media.local @@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.media.local
import android.content.Context
import android.net.Uri
import androidx.core.net.toUri
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.androidutils.file.getFileName
import io.element.android.libraries.androidutils.file.getFileSize
@ -25,7 +26,7 @@ import io.element.android.libraries.core.mimetype.MimeTypes @@ -25,7 +26,7 @@ import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.matrix.api.media.MediaFile
import java.io.File
import io.element.android.libraries.matrix.api.media.toFile
import javax.inject.Inject
@ContributesBinding(AppScope::class)
@ -34,16 +35,8 @@ class AndroidLocalMediaFactory @Inject constructor( @@ -34,16 +35,8 @@ class AndroidLocalMediaFactory @Inject constructor(
) : LocalMediaFactory {
override fun createFromMediaFile(mediaFile: MediaFile, mimeType: String?): LocalMedia {
val resolvedMimeType = mimeType ?: MimeTypes.OctetStream
val file = File(mediaFile.path())
val fileName = file.name
val fileSize = file.length()
return LocalMedia(
source = LocalMedia.Source.FromFile(file),
mimeType = resolvedMimeType,
name = fileName,
size = fileSize
)
val uri = mediaFile.toFile().toUri()
return createFromUri(uri, mimeType)
}
override fun createFromUri(uri: Uri, mimeType: String?): LocalMedia {
@ -51,7 +44,7 @@ class AndroidLocalMediaFactory @Inject constructor( @@ -51,7 +44,7 @@ class AndroidLocalMediaFactory @Inject constructor(
val fileName = context.getFileName(uri)
val fileSize = context.getFileSize(uri)
return LocalMedia(
source = LocalMedia.Source.FromUri(uri),
uri = uri,
mimeType = resolvedMimeType,
name = fileName,
size = fileSize

19
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMedia.kt

@ -21,27 +21,12 @@ import android.os.Parcelable @@ -21,27 +21,12 @@ import android.os.Parcelable
import androidx.compose.runtime.Immutable
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import java.io.File
@Parcelize
@Immutable
data class LocalMedia(
val source: Source,
val uri: Uri,
val mimeType: String,
val name: String?,
val size: Long,
) : Parcelable {
sealed interface Source : Parcelable {
@Parcelize
data class FromUri(val uri: Uri) : Source
@Parcelize
data class FromFile(val file: File) : Source
}
@IgnoredOnParcel val model: Any = when (source) {
is Source.FromUri -> source.uri
is Source.FromFile -> source.file
}
}
) : Parcelable

11
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt

@ -31,13 +31,13 @@ import androidx.compose.ui.platform.LocalInspectionMode @@ -31,13 +31,13 @@ import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.painterResource
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.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView
import io.element.android.features.messages.impl.media.local.exoplayer.ExoPlayerWrapper
import io.element.android.features.messages.impl.media.local.exoplayer.toMediaItem
import io.element.android.libraries.designsystem.R
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
import me.saket.telephoto.zoomable.ZoomSpec
@ -93,7 +93,7 @@ private fun MediaImageView( @@ -93,7 +93,7 @@ private fun MediaImageView(
ZoomableAsyncImage(
modifier = modifier.fillMaxSize(),
state = zoomableImageState,
model = localMedia?.model,
model = localMedia?.uri,
contentDescription = "Image",
contentScale = ContentScale.Fit,
)
@ -107,7 +107,6 @@ fun MediaVideoView( @@ -107,7 +107,6 @@ fun MediaVideoView(
onReady: () -> Unit,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val playerListener = object : Player.Listener {
override fun onRenderedFirstFrame() {
@ -121,9 +120,9 @@ fun MediaVideoView( @@ -121,9 +120,9 @@ fun MediaVideoView(
this.prepare()
}
}
if (localMedia?.source != null) {
LaunchedEffect(localMedia.source) {
val mediaItem = localMedia.toMediaItem()
if (localMedia?.uri != null) {
LaunchedEffect(localMedia.uri) {
val mediaItem = MediaItem.fromUri(localMedia.uri)
exoPlayer.setMediaItem(mediaItem)
}
} else {

27
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/exoplayer/LocalMediaExt.kt

@ -1,27 +0,0 @@ @@ -1,27 +0,0 @@
/*
* 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.local.exoplayer
import androidx.media3.common.MediaItem
import io.element.android.features.messages.impl.media.local.LocalMedia
fun LocalMedia.toMediaItem(): MediaItem {
return when (source) {
is LocalMedia.Source.FromFile -> MediaItem.fromUri(source.file.path)
is LocalMedia.Source.FromUri -> MediaItem.fromUri(source.uri)
}
}

6
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt

@ -16,11 +16,11 @@ @@ -16,11 +16,11 @@
package io.element.android.features.messages.impl.media.viewer
import android.net.Uri
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.media3.common.MimeTypes
import io.element.android.features.messages.impl.media.local.LocalMedia
import io.element.android.libraries.architecture.Async
import java.io.File
open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState> {
override val values: Sequence<MediaViewerState>
@ -31,14 +31,14 @@ open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState> @@ -31,14 +31,14 @@ open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState>
aMediaViewerState(
Async.Success(
LocalMedia(
LocalMedia.Source.FromFile(File("")), MimeTypes.IMAGE_JPEG, "an image file", 100L
Uri.EMPTY, MimeTypes.IMAGE_JPEG, "an image file", 100L
)
),
),
aMediaViewerState(
Async.Success(
LocalMedia(
LocalMedia.Source.FromFile(File("")), MimeTypes.VIDEO_MP4, "a video file", 100L
Uri.EMPTY, MimeTypes.VIDEO_MP4, "a video file", 100L
)
),
)

11
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt

@ -31,7 +31,6 @@ import androidx.media3.common.MimeTypes @@ -31,7 +31,6 @@ import androidx.media3.common.MimeTypes
import androidx.media3.common.util.UnstableApi
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.LocalMedia
import io.element.android.features.messages.impl.media.local.LocalMediaFactory
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.data.StableCharSequence
@ -192,7 +191,8 @@ class MessageComposerPresenter @Inject constructor( @@ -192,7 +191,8 @@ class MessageComposerPresenter @Inject constructor(
when (attachment) {
is Attachment.Media -> {
sendMedia(
media = attachment.localMedia,
uri = attachment.localMedia.uri,
mimeType = attachment.localMedia.mimeType,
attachmentState = attachmentState
)
}
@ -226,12 +226,11 @@ class MessageComposerPresenter @Inject constructor( @@ -226,12 +226,11 @@ class MessageComposerPresenter @Inject constructor(
}
private suspend fun sendMedia(
media: LocalMedia,
uri: Uri,
mimeType: String,
attachmentState: MutableState<AttachmentsState>,
) {
if (media.source !is LocalMedia.Source.FromUri) error("Attachment should use Uri")
val uri = media.source.uri
mediaSender.sendMedia(uri, media.mimeType, compressIfPossible = false)
mediaSender.sendMedia(uri, mimeType, compressIfPossible = false)
.onSuccess {
attachmentState.value = AttachmentsState.None
}.onFailure {

3
features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt

@ -20,6 +20,7 @@ import android.net.Uri @@ -20,6 +20,7 @@ import android.net.Uri
import androidx.media3.common.MimeTypes
import io.element.android.features.messages.impl.attachments.Attachment
import io.element.android.features.messages.impl.media.local.LocalMedia
import io.mockk.mockk
fun aLocalMedia(
uri: Uri,
@ -27,7 +28,7 @@ fun aLocalMedia( @@ -27,7 +28,7 @@ fun aLocalMedia(
name: String = "a media",
size: Long = 1000,
) = LocalMedia(
source = LocalMedia.Source.FromUri(uri),
uri = uri,
mimeType = mimeType,
name = name,
size = size,

5
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaFile.kt

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
package io.element.android.libraries.matrix.api.media
import java.io.Closeable
import java.io.File
/**
* A wrapper around a media file on the disk.
@ -25,3 +26,7 @@ import java.io.Closeable @@ -25,3 +26,7 @@ import java.io.Closeable
interface MediaFile : Closeable {
fun path(): String
}
fun MediaFile.toFile(): File {
return File(path())
}

Loading…
Cancel
Save