Media: improve a bit the viewers
This commit is contained in:
parent
fc601acd28
commit
92e19c3dd9
11 changed files with 71 additions and 28 deletions
|
|
@ -29,13 +29,12 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider<Attachment
|
|||
anAttachmentsPreviewState(),
|
||||
anAttachmentsPreviewState(sendActionState = Async.Loading()),
|
||||
anAttachmentsPreviewState(sendActionState = Async.Failure(RuntimeException())),
|
||||
// Add other states here
|
||||
)
|
||||
}
|
||||
|
||||
fun anAttachmentsPreviewState(sendActionState: Async<Unit> = Async.Uninitialized) = AttachmentsPreviewState(
|
||||
attachment = Attachment.Media(
|
||||
localMedia = LocalMedia("".toUri(), mimeType = MimeTypes.OctetStream),
|
||||
localMedia = LocalMedia("path".toUri(), MimeTypes.Jpeg, "an image", 1000L),
|
||||
compressIfPossible = true
|
||||
),
|
||||
sendActionState = sendActionState,
|
||||
|
|
|
|||
|
|
@ -79,13 +79,13 @@ fun AttachmentsPreviewView(
|
|||
onSendClicked = ::postSendAttachment,
|
||||
onDismiss = onDismiss
|
||||
)
|
||||
AttachmentSendStateView(
|
||||
sendActionState = state.sendActionState,
|
||||
onRetryClicked = ::postSendAttachment,
|
||||
onRetryDismissed = ::postClearSendState
|
||||
)
|
||||
}
|
||||
}
|
||||
AttachmentSendStateView(
|
||||
sendActionState = state.sendActionState,
|
||||
onRetryClicked = ::postSendAttachment,
|
||||
onRetryDismissed = ::postClearSendState
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -106,7 +106,6 @@ private fun AttachmentSendStateView(
|
|||
onRetry = onRetryClicked
|
||||
)
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
|
@ -115,10 +114,11 @@ private fun AttachmentSendStateView(
|
|||
private fun AttachmentPreviewContent(
|
||||
attachment: Attachment,
|
||||
onSendClicked: () -> Unit,
|
||||
onDismiss: () -> Unit
|
||||
onDismiss: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(top = 24.dp)
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ 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
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
|
|
@ -32,13 +34,19 @@ class AndroidLocalMediaFactory @Inject constructor(
|
|||
) : LocalMediaFactory {
|
||||
|
||||
override fun createFromMediaFile(mediaFile: MediaFile, mimeType: String?): LocalMedia {
|
||||
val resolvedMimeType = mimeType ?: MimeTypes.OctetStream
|
||||
val uri = mediaFile.path().toUri()
|
||||
return LocalMedia(uri, resolvedMimeType)
|
||||
return createFromUri(uri, mimeType)
|
||||
}
|
||||
|
||||
override fun createFromUri(uri: Uri, mimeType: String?): LocalMedia {
|
||||
val resolvedMimeType = mimeType ?: context.contentResolver.getType(uri) ?: MimeTypes.OctetStream
|
||||
return LocalMedia(uri, resolvedMimeType)
|
||||
val fileName = context.getFileName(uri)
|
||||
val fileSize = context.getFileSize(uri)
|
||||
return LocalMedia(
|
||||
uri = uri,
|
||||
mimeType = resolvedMimeType,
|
||||
name = fileName,
|
||||
size = fileSize
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import kotlinx.parcelize.Parcelize
|
|||
data class LocalMedia(
|
||||
val uri: Uri,
|
||||
val mimeType: String,
|
||||
val name: String?,
|
||||
val size: Long,
|
||||
) : Parcelable {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.media.local
|
|||
import android.annotation.SuppressLint
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.widget.FrameLayout
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
|
@ -26,6 +27,8 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
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
|
||||
|
|
@ -34,6 +37,7 @@ import androidx.media3.common.util.UnstableApi
|
|||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.ui.AspectRatioFrameLayout
|
||||
import androidx.media3.ui.PlayerView
|
||||
import io.element.android.libraries.designsystem.R
|
||||
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
|
||||
import me.saket.telephoto.zoomable.ZoomSpec
|
||||
import me.saket.telephoto.zoomable.coil.ZoomableAsyncImage
|
||||
|
|
@ -64,6 +68,13 @@ private fun MediaImageView(
|
|||
localMedia: LocalMedia,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (LocalInspectionMode.current) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.sample_background),
|
||||
modifier = modifier.fillMaxSize(),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
val zoomableState = rememberZoomableState(
|
||||
zoomSpec = ZoomSpec(maxZoomFactor = 3f)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ object UriToFileMapper {
|
|||
}
|
||||
|
||||
private fun isApplicable(data: Uri): Boolean {
|
||||
return data.scheme.let { it == null || it == ContentResolver.SCHEME_FILE } &&
|
||||
return !isAssetUri(data) &&
|
||||
data.scheme.let { it == null || it == ContentResolver.SCHEME_FILE } &&
|
||||
data.path.orEmpty().startsWith('/') && data.firstPathSegment != null
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,19 +16,30 @@
|
|||
|
||||
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
|
||||
|
||||
open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState> {
|
||||
override val values: Sequence<MediaViewerState>
|
||||
get() = sequenceOf(
|
||||
aMediaViewerState(),
|
||||
// Add other states here
|
||||
aMediaViewerState(Async.Loading()),
|
||||
aMediaViewerState(Async.Failure(IllegalStateException())),
|
||||
aMediaViewerState(
|
||||
Async.Success(
|
||||
LocalMedia(
|
||||
Uri.EMPTY, MimeTypes.IMAGE_JPEG, "a file", 100L
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun aMediaViewerState() = MediaViewerState(
|
||||
fun aMediaViewerState(downloadedMedia: Async<LocalMedia> = Async.Uninitialized) = MediaViewerState(
|
||||
name = "A media",
|
||||
downloadedMedia = Async.Uninitialized,
|
||||
downloadedMedia = downloadedMedia,
|
||||
eventSink = {}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -63,9 +63,7 @@ fun MediaViewerView(
|
|||
when (state.downloadedMedia) {
|
||||
is Async.Success -> LocalMediaView(state.downloadedMedia.state)
|
||||
is Async.Failure -> ErrorView(stringResource(id = StringR.string.error_unknown), ::onRetry)
|
||||
else -> CircularProgressIndicator(
|
||||
strokeWidth = 2.dp,
|
||||
)
|
||||
else -> CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -92,11 +90,6 @@ private fun ErrorView(
|
|||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun MediaViewerViewLightPreview(@PreviewParameter(MediaViewerStateProvider::class) state: MediaViewerState) =
|
||||
ElementPreviewLight { ContentToPreview(state) }
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun MediaViewerViewDarkPreview(@PreviewParameter(MediaViewerStateProvider::class) state: MediaViewerState) =
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ class AttachmentsPreviewPresenterTest {
|
|||
}
|
||||
|
||||
private fun anAttachmentsPreviewPresenter(
|
||||
localMedia: LocalMedia = aLocalMedia(mimeType = MimeTypes.IMAGE_JPEG),
|
||||
localMedia: LocalMedia = aLocalMedia(MimeTypes.IMAGE_JPEG),
|
||||
room: MatrixRoom = FakeMatrixRoom()
|
||||
): AttachmentsPreviewPresenter {
|
||||
return AttachmentsPreviewPresenter(
|
||||
|
|
|
|||
|
|
@ -22,11 +22,15 @@ import io.element.android.features.messages.impl.media.local.LocalMedia
|
|||
import io.mockk.mockk
|
||||
|
||||
fun aLocalMedia(
|
||||
mimeType: String,
|
||||
uri: Uri = mockk("localMediaUri"),
|
||||
mimeType: String
|
||||
name: String = "a media",
|
||||
size: Long = 1000,
|
||||
) = LocalMedia(
|
||||
uri = uri,
|
||||
mimeType = mimeType
|
||||
mimeType = mimeType,
|
||||
name = name,
|
||||
size = size,
|
||||
)
|
||||
|
||||
fun aMediaAttachment(localMedia: LocalMedia, compressIfPossible: Boolean = true) = Attachment.Media(
|
||||
|
|
|
|||
|
|
@ -27,6 +27,20 @@ fun Context.getFileName(uri: Uri): String? = when (uri.scheme) {
|
|||
else -> uri.path?.let(::File)?.name
|
||||
}
|
||||
|
||||
fun Context.getFileSize(uri: Uri): Long {
|
||||
return when (uri.scheme) {
|
||||
ContentResolver.SCHEME_CONTENT -> getContentFileSize(uri)
|
||||
else -> uri.path?.let(::File)?.length()
|
||||
} ?: 0
|
||||
}
|
||||
|
||||
private fun Context.getContentFileSize(uri: Uri): Long? = runCatching {
|
||||
contentResolver.query(uri, null, null, null, null)?.use { cursor ->
|
||||
cursor.moveToFirst()
|
||||
return@use cursor.getColumnIndexOrThrow(OpenableColumns.SIZE).let(cursor::getLong)
|
||||
}
|
||||
}.getOrNull()
|
||||
|
||||
private fun Context.getContentFileName(uri: Uri): String? = runCatching {
|
||||
contentResolver.query(uri, null, null, null, null)?.use { cursor ->
|
||||
cursor.moveToFirst()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue