MediaViewer: iterate on design
This commit is contained in:
@@ -337,7 +337,9 @@ class MessagesFlowNode @AssistedInject constructor(
|
||||
caption = event.content.caption,
|
||||
mimeType = event.content.mimeType,
|
||||
formattedFileSize = event.content.formattedFileSize,
|
||||
fileExtension = event.content.fileExtension
|
||||
fileExtension = event.content.fileExtension,
|
||||
senderName = event.safeSenderName,
|
||||
dateSent = event.sentTime,
|
||||
),
|
||||
mediaSource = event.content.mediaSource,
|
||||
thumbnailSource = event.content.thumbnailSource,
|
||||
@@ -355,7 +357,9 @@ class MessagesFlowNode @AssistedInject constructor(
|
||||
caption = event.content.caption,
|
||||
mimeType = event.content.mimeType,
|
||||
formattedFileSize = event.content.formattedFileSize,
|
||||
fileExtension = event.content.fileExtension
|
||||
fileExtension = event.content.fileExtension,
|
||||
senderName = event.safeSenderName,
|
||||
dateSent = event.sentTime,
|
||||
),
|
||||
mediaSource = event.content.preferredMediaSource,
|
||||
thumbnailSource = event.content.thumbnailSource,
|
||||
@@ -373,7 +377,9 @@ class MessagesFlowNode @AssistedInject constructor(
|
||||
caption = event.content.caption,
|
||||
mimeType = event.content.mimeType,
|
||||
formattedFileSize = event.content.formattedFileSize,
|
||||
fileExtension = event.content.fileExtension
|
||||
fileExtension = event.content.fileExtension,
|
||||
senderName = event.safeSenderName,
|
||||
dateSent = event.sentTime,
|
||||
),
|
||||
mediaSource = event.content.videoSource,
|
||||
thumbnailSource = event.content.thumbnailSource,
|
||||
@@ -388,7 +394,9 @@ class MessagesFlowNode @AssistedInject constructor(
|
||||
caption = event.content.caption,
|
||||
mimeType = event.content.mimeType,
|
||||
formattedFileSize = event.content.formattedFileSize,
|
||||
fileExtension = event.content.fileExtension
|
||||
fileExtension = event.content.fileExtension,
|
||||
senderName = event.safeSenderName,
|
||||
dateSent = event.sentTime,
|
||||
),
|
||||
mediaSource = event.content.fileSource,
|
||||
thumbnailSource = event.content.thumbnailSource,
|
||||
@@ -403,7 +411,9 @@ class MessagesFlowNode @AssistedInject constructor(
|
||||
caption = event.content.caption,
|
||||
mimeType = event.content.mimeType,
|
||||
formattedFileSize = event.content.formattedFileSize,
|
||||
fileExtension = event.content.fileExtension
|
||||
fileExtension = event.content.fileExtension,
|
||||
senderName = event.safeSenderName,
|
||||
dateSent = event.sentTime,
|
||||
),
|
||||
mediaSource = event.content.mediaSource,
|
||||
thumbnailSource = null,
|
||||
|
||||
@@ -18,44 +18,73 @@ data class MediaInfo(
|
||||
val mimeType: String,
|
||||
val formattedFileSize: String,
|
||||
val fileExtension: String,
|
||||
val senderName: String?,
|
||||
val dateSent: String?,
|
||||
) : Parcelable
|
||||
|
||||
fun anImageMediaInfo(): MediaInfo = MediaInfo(
|
||||
fun anImageMediaInfo(
|
||||
caption: String? = null,
|
||||
senderName: String? = null,
|
||||
dateSent: String? = null,
|
||||
): MediaInfo = MediaInfo(
|
||||
filename = "an image file.jpg",
|
||||
caption = null,
|
||||
caption = caption,
|
||||
mimeType = MimeTypes.Jpeg,
|
||||
formattedFileSize = "4MB",
|
||||
fileExtension = "jpg",
|
||||
senderName = senderName,
|
||||
dateSent = dateSent,
|
||||
)
|
||||
|
||||
fun aVideoMediaInfo(): MediaInfo = MediaInfo(
|
||||
fun aVideoMediaInfo(
|
||||
caption: String? = null,
|
||||
senderName: String? = null,
|
||||
dateSent: String? = null,
|
||||
): MediaInfo = MediaInfo(
|
||||
filename = "a video file.mp4",
|
||||
caption = null,
|
||||
caption = caption,
|
||||
mimeType = MimeTypes.Mp4,
|
||||
formattedFileSize = "14MB",
|
||||
fileExtension = "mp4",
|
||||
senderName = senderName,
|
||||
dateSent = dateSent,
|
||||
)
|
||||
|
||||
fun aPdfMediaInfo(): MediaInfo = MediaInfo(
|
||||
fun aPdfMediaInfo(
|
||||
senderName: String? = null,
|
||||
dateSent: String? = null,
|
||||
): MediaInfo = MediaInfo(
|
||||
filename = "a pdf file.pdf",
|
||||
caption = null,
|
||||
mimeType = MimeTypes.Pdf,
|
||||
formattedFileSize = "23MB",
|
||||
fileExtension = "pdf",
|
||||
senderName = senderName,
|
||||
dateSent = dateSent,
|
||||
)
|
||||
|
||||
fun anApkMediaInfo(): MediaInfo = MediaInfo(
|
||||
fun anApkMediaInfo(
|
||||
senderName: String? = null,
|
||||
dateSent: String? = null,
|
||||
): MediaInfo = MediaInfo(
|
||||
filename = "an apk file.apk",
|
||||
caption = null,
|
||||
mimeType = MimeTypes.Apk,
|
||||
formattedFileSize = "50MB",
|
||||
fileExtension = "apk",
|
||||
senderName = senderName,
|
||||
dateSent = dateSent,
|
||||
)
|
||||
|
||||
fun anAudioMediaInfo(): MediaInfo = MediaInfo(
|
||||
fun anAudioMediaInfo(
|
||||
senderName: String? = null,
|
||||
dateSent: String? = null,
|
||||
): MediaInfo = MediaInfo(
|
||||
filename = "an audio file.mp3",
|
||||
caption = null,
|
||||
mimeType = MimeTypes.Mp3,
|
||||
formattedFileSize = "7MB",
|
||||
fileExtension = "mp3",
|
||||
senderName = senderName,
|
||||
dateSent = dateSent,
|
||||
)
|
||||
|
||||
@@ -46,7 +46,9 @@ class DefaultMediaViewerEntryPoint @Inject constructor() : MediaViewerEntryPoint
|
||||
caption = null,
|
||||
mimeType = mimeType,
|
||||
formattedFileSize = "",
|
||||
fileExtension = ""
|
||||
fileExtension = "",
|
||||
senderName = null,
|
||||
dateSent = null,
|
||||
),
|
||||
mediaSource = MediaSource(url = avatarUrl),
|
||||
thumbnailSource = null,
|
||||
|
||||
@@ -41,6 +41,8 @@ class AndroidLocalMediaFactory @Inject constructor(
|
||||
name = mediaInfo.filename,
|
||||
caption = mediaInfo.caption,
|
||||
formattedFileSize = mediaInfo.formattedFileSize,
|
||||
senderName = mediaInfo.senderName,
|
||||
dateSent = mediaInfo.dateSent,
|
||||
)
|
||||
|
||||
override fun createFromUri(
|
||||
@@ -54,6 +56,8 @@ class AndroidLocalMediaFactory @Inject constructor(
|
||||
name = name,
|
||||
caption = null,
|
||||
formattedFileSize = formattedFileSize,
|
||||
senderName = null,
|
||||
dateSent = null,
|
||||
)
|
||||
|
||||
private fun createFromUri(
|
||||
@@ -61,7 +65,9 @@ class AndroidLocalMediaFactory @Inject constructor(
|
||||
mimeType: String?,
|
||||
name: String?,
|
||||
caption: String?,
|
||||
formattedFileSize: String?
|
||||
formattedFileSize: String?,
|
||||
senderName: String?,
|
||||
dateSent: String?,
|
||||
): LocalMedia {
|
||||
val resolvedMimeType = mimeType ?: context.getMimeType(uri) ?: MimeTypes.OctetStream
|
||||
val fileName = name ?: context.getFileName(uri) ?: ""
|
||||
@@ -74,7 +80,9 @@ class AndroidLocalMediaFactory @Inject constructor(
|
||||
filename = fileName,
|
||||
caption = caption,
|
||||
formattedFileSize = fileSize,
|
||||
fileExtension = fileExtension
|
||||
fileExtension = fileExtension,
|
||||
senderName = senderName,
|
||||
dateSent = dateSent,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ class DefaultLocalMediaRenderer @Inject constructor() : LocalMediaRenderer {
|
||||
)
|
||||
LocalMediaView(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
bottomPaddingInPixels = 0,
|
||||
localMedia = localMedia,
|
||||
localMediaViewState = localMediaViewState,
|
||||
onClick = {}
|
||||
|
||||
@@ -22,6 +22,7 @@ import io.element.android.libraries.mediaviewer.impl.local.video.MediaVideoView
|
||||
@Composable
|
||||
fun LocalMediaView(
|
||||
localMedia: LocalMedia?,
|
||||
bottomPaddingInPixels: Int,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
localMediaViewState: LocalMediaViewState = rememberLocalMediaViewState(),
|
||||
@@ -37,6 +38,7 @@ fun LocalMediaView(
|
||||
)
|
||||
mimeType.isMimeTypeVideo() -> MediaVideoView(
|
||||
localMediaViewState = localMediaViewState,
|
||||
bottomPaddingInPixels = bottomPaddingInPixels,
|
||||
localMedia = localMedia,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
||||
@@ -14,6 +14,7 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
@@ -37,6 +38,7 @@ import androidx.media3.ui.PlayerView
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.text.toDp
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.utils.KeepScreenOn
|
||||
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
|
||||
@@ -51,6 +53,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||
@Composable
|
||||
fun MediaVideoView(
|
||||
localMediaViewState: LocalMediaViewState,
|
||||
bottomPaddingInPixels: Int,
|
||||
localMedia: LocalMedia?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -66,6 +69,7 @@ fun MediaVideoView(
|
||||
}
|
||||
ExoPlayerMediaVideoView(
|
||||
localMediaViewState = localMediaViewState,
|
||||
bottomPaddingInPixels = bottomPaddingInPixels,
|
||||
exoPlayer = exoPlayer,
|
||||
localMedia = localMedia,
|
||||
modifier = modifier,
|
||||
@@ -76,6 +80,7 @@ fun MediaVideoView(
|
||||
@Composable
|
||||
private fun ExoPlayerMediaVideoView(
|
||||
localMediaViewState: LocalMediaViewState,
|
||||
bottomPaddingInPixels: Int,
|
||||
exoPlayer: ExoPlayer,
|
||||
localMedia: LocalMedia?,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -232,7 +237,8 @@ private fun ExoPlayerMediaVideoView(
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.BottomCenter),
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(bottom = bottomPaddingInPixels.toDp()),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -254,6 +260,7 @@ private fun ExoPlayerMediaVideoView(
|
||||
internal fun MediaVideoViewPreview() = ElementPreview {
|
||||
MediaVideoView(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
bottomPaddingInPixels = 0,
|
||||
localMediaViewState = rememberLocalMediaViewState(),
|
||||
localMedia = null,
|
||||
)
|
||||
|
||||
@@ -24,52 +24,72 @@ open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState>
|
||||
aMediaViewerState(),
|
||||
aMediaViewerState(AsyncData.Loading()),
|
||||
aMediaViewerState(AsyncData.Failure(IllegalStateException("error"))),
|
||||
aMediaViewerState(
|
||||
AsyncData.Success(
|
||||
LocalMedia(Uri.EMPTY, anImageMediaInfo())
|
||||
),
|
||||
anImageMediaInfo(),
|
||||
),
|
||||
aMediaViewerState(
|
||||
AsyncData.Success(
|
||||
LocalMedia(Uri.EMPTY, aVideoMediaInfo())
|
||||
),
|
||||
aVideoMediaInfo(),
|
||||
),
|
||||
aMediaViewerState(
|
||||
AsyncData.Success(
|
||||
LocalMedia(Uri.EMPTY, aPdfMediaInfo())
|
||||
),
|
||||
aPdfMediaInfo(),
|
||||
),
|
||||
anImageMediaInfo(
|
||||
senderName = "Sally Sanderson",
|
||||
dateSent = "21 NOV, 2024",
|
||||
caption = "A caption",
|
||||
).let {
|
||||
aMediaViewerState(
|
||||
AsyncData.Success(
|
||||
LocalMedia(Uri.EMPTY, it)
|
||||
),
|
||||
it,
|
||||
)
|
||||
},
|
||||
aVideoMediaInfo(
|
||||
senderName = "Sally Sanderson",
|
||||
dateSent = "21 NOV, 2024",
|
||||
caption = "A caption",
|
||||
).let {
|
||||
aMediaViewerState(
|
||||
AsyncData.Success(
|
||||
LocalMedia(Uri.EMPTY, it)
|
||||
),
|
||||
it,
|
||||
)
|
||||
},
|
||||
aPdfMediaInfo().let {
|
||||
aMediaViewerState(
|
||||
AsyncData.Success(
|
||||
LocalMedia(Uri.EMPTY, it)
|
||||
),
|
||||
it,
|
||||
)
|
||||
},
|
||||
aMediaViewerState(
|
||||
AsyncData.Loading(),
|
||||
anApkMediaInfo(),
|
||||
),
|
||||
aMediaViewerState(
|
||||
AsyncData.Success(
|
||||
LocalMedia(Uri.EMPTY, anApkMediaInfo())
|
||||
),
|
||||
anApkMediaInfo(),
|
||||
),
|
||||
anApkMediaInfo().let {
|
||||
aMediaViewerState(
|
||||
AsyncData.Success(
|
||||
LocalMedia(Uri.EMPTY, it)
|
||||
),
|
||||
it,
|
||||
)
|
||||
},
|
||||
aMediaViewerState(
|
||||
AsyncData.Loading(),
|
||||
anAudioMediaInfo(),
|
||||
),
|
||||
aMediaViewerState(
|
||||
AsyncData.Success(
|
||||
LocalMedia(Uri.EMPTY, anAudioMediaInfo())
|
||||
),
|
||||
anAudioMediaInfo(),
|
||||
),
|
||||
aMediaViewerState(
|
||||
AsyncData.Success(
|
||||
LocalMedia(Uri.EMPTY, anImageMediaInfo())
|
||||
),
|
||||
anImageMediaInfo(),
|
||||
canDownload = false,
|
||||
canShare = false,
|
||||
),
|
||||
anAudioMediaInfo().let {
|
||||
aMediaViewerState(
|
||||
AsyncData.Success(
|
||||
LocalMedia(Uri.EMPTY, it)
|
||||
),
|
||||
it,
|
||||
)
|
||||
},
|
||||
anImageMediaInfo().let {
|
||||
aMediaViewerState(
|
||||
AsyncData.Success(
|
||||
LocalMedia(Uri.EMPTY, it)
|
||||
),
|
||||
it,
|
||||
canDownload = false,
|
||||
canShare = false,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,14 @@ import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -28,6 +32,7 @@ import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
@@ -36,12 +41,15 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
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.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
@@ -51,6 +59,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.IconButton
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
|
||||
@@ -80,6 +89,7 @@ fun MediaViewerView(
|
||||
val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage)
|
||||
var showOverlay by remember { mutableStateOf(true) }
|
||||
|
||||
var bottomPaddingInPixels by remember { mutableIntStateOf(0) }
|
||||
BackHandler { onBackClick() }
|
||||
Scaffold(
|
||||
modifier,
|
||||
@@ -88,6 +98,7 @@ fun MediaViewerView(
|
||||
) {
|
||||
MediaViewerPage(
|
||||
showOverlay = showOverlay,
|
||||
bottomPaddingInPixels = bottomPaddingInPixels,
|
||||
state = state,
|
||||
onDismiss = {
|
||||
onBackClick()
|
||||
@@ -97,14 +108,29 @@ fun MediaViewerView(
|
||||
}
|
||||
)
|
||||
AnimatedVisibility(visible = showOverlay, enter = fadeIn(), exit = fadeOut()) {
|
||||
MediaViewerTopBar(
|
||||
actionsEnabled = state.downloadedMedia is AsyncData.Success,
|
||||
mimeType = state.mediaInfo.mimeType,
|
||||
onBackClick = onBackClick,
|
||||
canDownload = state.canDownload,
|
||||
canShare = state.canShare,
|
||||
eventSink = state.eventSink
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding()
|
||||
) {
|
||||
MediaViewerTopBar(
|
||||
actionsEnabled = state.downloadedMedia is AsyncData.Success,
|
||||
senderName = state.mediaInfo.senderName,
|
||||
dateSent = state.mediaInfo.dateSent,
|
||||
onBackClick = onBackClick,
|
||||
eventSink = state.eventSink
|
||||
)
|
||||
MediaViewerBottomBar(
|
||||
modifier = Modifier.align(Alignment.BottomCenter),
|
||||
actionsEnabled = state.downloadedMedia is AsyncData.Success,
|
||||
canDownload = state.canDownload,
|
||||
canShare = state.canShare,
|
||||
mimeType = state.mediaInfo.mimeType,
|
||||
caption = state.mediaInfo.caption,
|
||||
onHeightChanged = { bottomPaddingInPixels = it },
|
||||
eventSink = state.eventSink
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,6 +138,7 @@ fun MediaViewerView(
|
||||
@Composable
|
||||
private fun MediaViewerPage(
|
||||
showOverlay: Boolean,
|
||||
bottomPaddingInPixels: Int,
|
||||
state: MediaViewerState,
|
||||
onDismiss: () -> Unit,
|
||||
onShowOverlayChange: (Boolean) -> Unit,
|
||||
@@ -148,8 +175,8 @@ private fun MediaViewerPage(
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding()
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding()
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
val zoomableState = rememberZoomableState(
|
||||
@@ -168,6 +195,7 @@ private fun MediaViewerPage(
|
||||
|
||||
LocalMediaView(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
bottomPaddingInPixels = bottomPaddingInPixels,
|
||||
localMediaViewState = localMediaViewState,
|
||||
localMedia = state.downloadedMedia.dataOrNull(),
|
||||
mediaInfo = state.mediaInfo,
|
||||
@@ -193,8 +221,8 @@ private fun MediaViewerPage(
|
||||
if (showProgress) {
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(2.dp)
|
||||
.fillMaxWidth()
|
||||
.height(2.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -246,23 +274,99 @@ private fun rememberShowProgress(downloadedMedia: AsyncData<LocalMedia>): Boolea
|
||||
return showProgress
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun MediaViewerTopBar(
|
||||
actionsEnabled: Boolean,
|
||||
canDownload: Boolean,
|
||||
canShare: Boolean,
|
||||
mimeType: String,
|
||||
senderName: String?,
|
||||
dateSent: String?,
|
||||
onBackClick: () -> Unit,
|
||||
eventSink: (MediaViewerEvents) -> Unit,
|
||||
) {
|
||||
TopAppBar(
|
||||
title = {},
|
||||
title = {
|
||||
if (senderName != null && dateSent != null) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = 48.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = senderName,
|
||||
style = ElementTheme.typography.fontBodyMdMedium,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
Text(
|
||||
text = dateSent,
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = Color.Transparent.copy(0.6f),
|
||||
),
|
||||
navigationIcon = { BackButton(onClick = onBackClick) },
|
||||
actions = {
|
||||
// TODO Add action to open infos.
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MediaViewerBottomBar(
|
||||
actionsEnabled: Boolean,
|
||||
canDownload: Boolean,
|
||||
canShare: Boolean,
|
||||
mimeType: String,
|
||||
caption: String?,
|
||||
onHeightChanged: (Int) -> Unit,
|
||||
eventSink: (MediaViewerEvents) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(Color(0x99101317))
|
||||
.onSizeChanged {
|
||||
onHeightChanged(it.height)
|
||||
},
|
||||
) {
|
||||
if (caption != null) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
text = caption,
|
||||
maxLines = 5,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 8.dp, end = 8.dp, bottom = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (canShare) {
|
||||
IconButton(
|
||||
enabled = actionsEnabled,
|
||||
onClick = {
|
||||
eventSink(MediaViewerEvents.Share)
|
||||
},
|
||||
modifier = Modifier.align(Alignment.CenterVertically)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.ShareAndroid(),
|
||||
contentDescription = stringResource(id = CommonStrings.action_share)
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
IconButton(
|
||||
enabled = actionsEnabled,
|
||||
onClick = {
|
||||
@@ -293,21 +397,8 @@ private fun MediaViewerTopBar(
|
||||
)
|
||||
}
|
||||
}
|
||||
if (canShare) {
|
||||
IconButton(
|
||||
enabled = actionsEnabled,
|
||||
onClick = {
|
||||
eventSink(MediaViewerEvents.Share)
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.ShareAndroid(),
|
||||
contentDescription = stringResource(id = CommonStrings.action_share)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||
import io.element.android.libraries.matrix.test.media.FakeMediaFile
|
||||
import io.element.android.libraries.mediaviewer.api.MediaInfo
|
||||
import io.element.android.libraries.mediaviewer.api.anImageMediaInfo
|
||||
@@ -25,7 +26,10 @@ class AndroidLocalMediaFactoryTest {
|
||||
@Test
|
||||
fun `test AndroidLocalMediaFactory`() {
|
||||
val sut = createAndroidLocalMediaFactory()
|
||||
val result = sut.createFromMediaFile(aMediaFile(), anImageMediaInfo())
|
||||
val result = sut.createFromMediaFile(aMediaFile(), anImageMediaInfo(
|
||||
senderName = A_USER_NAME,
|
||||
dateSent = "12:34",
|
||||
))
|
||||
assertThat(result.uri.toString()).endsWith("aPath")
|
||||
assertThat(result.info).isEqualTo(
|
||||
MediaInfo(
|
||||
@@ -34,6 +38,8 @@ class AndroidLocalMediaFactoryTest {
|
||||
mimeType = MimeTypes.Jpeg,
|
||||
formattedFileSize = "4MB",
|
||||
fileExtension = "jpg",
|
||||
senderName = A_USER_NAME,
|
||||
dateSent = "12:34"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -36,7 +36,9 @@ class FakeLocalMediaFactory(
|
||||
caption = null,
|
||||
mimeType = mimeType ?: fallbackMimeType,
|
||||
formattedFileSize = formattedFileSize ?: fallbackFileSize,
|
||||
fileExtension = fileExtensionExtractor.extractFromName(safeName)
|
||||
fileExtension = fileExtensionExtractor.extractFromName(safeName),
|
||||
senderName = null,
|
||||
dateSent = null
|
||||
)
|
||||
return aLocalMedia(uri, mediaInfo)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user