Provide duration
This commit is contained in:
committed by
Benoit Marty
parent
24a2458e4a
commit
03523c9567
@@ -48,6 +48,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.duration
|
||||
import io.element.android.features.poll.api.create.CreatePollEntryPoint
|
||||
import io.element.android.features.poll.api.create.CreatePollMode
|
||||
import io.element.android.libraries.architecture.BackstackWithOverlayBox
|
||||
@@ -58,6 +59,7 @@ import io.element.android.libraries.architecture.overlay.operation.hide
|
||||
import io.element.android.libraries.architecture.overlay.operation.show
|
||||
import io.element.android.libraries.dateformatter.api.DateFormatter
|
||||
import io.element.android.libraries.dateformatter.api.DateFormatterMode
|
||||
import io.element.android.libraries.dateformatter.api.toHumanReadableDuration
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
@@ -449,6 +451,7 @@ class MessagesFlowNode @AssistedInject constructor(
|
||||
mode = DateFormatterMode.Full,
|
||||
),
|
||||
waveform = (content as? TimelineItemVoiceContent)?.waveform,
|
||||
duration = content.duration()?.toHumanReadableDuration(),
|
||||
),
|
||||
mediaSource = mediaSource,
|
||||
thumbnailSource = thumbnailSource,
|
||||
|
||||
@@ -9,6 +9,7 @@ package io.element.android.features.messages.impl.timeline.model.event
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import kotlin.time.Duration
|
||||
|
||||
@Immutable
|
||||
sealed interface TimelineItemEventContent {
|
||||
@@ -90,3 +91,12 @@ fun TimelineItemEventContent.isEdited(): Boolean = when (this) {
|
||||
is TimelineItemEventMutableContent -> isEdited
|
||||
else -> false
|
||||
}
|
||||
|
||||
fun TimelineItemEventContentWithAttachment.duration(): Duration? {
|
||||
return when (this) {
|
||||
is TimelineItemAudioContent -> duration
|
||||
is TimelineItemVideoContent -> duration
|
||||
is TimelineItemVoiceContent -> duration
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package io.element.android.libraries.dateformatter.api
|
||||
|
||||
import java.util.Locale
|
||||
import kotlin.time.Duration
|
||||
|
||||
/**
|
||||
* Convert milliseconds to human readable duration.
|
||||
@@ -38,3 +39,5 @@ fun Long.toHumanReadableDuration(): String {
|
||||
String.format(Locale.US, "%d:%02d", minutes, seconds)
|
||||
}
|
||||
}
|
||||
|
||||
fun Duration.toHumanReadableDuration() = inWholeMilliseconds.toHumanReadableDuration()
|
||||
|
||||
@@ -25,6 +25,7 @@ data class MediaInfo(
|
||||
val dateSent: String?,
|
||||
val dateSentFull: String?,
|
||||
val waveform: List<Float>?,
|
||||
val duration: String?,
|
||||
) : Parcelable
|
||||
|
||||
fun anImageMediaInfo(
|
||||
@@ -45,6 +46,7 @@ fun anImageMediaInfo(
|
||||
dateSent = dateSent,
|
||||
dateSentFull = dateSentFull,
|
||||
waveform = null,
|
||||
duration = null,
|
||||
)
|
||||
|
||||
fun aVideoMediaInfo(
|
||||
@@ -52,6 +54,7 @@ fun aVideoMediaInfo(
|
||||
senderName: String? = null,
|
||||
dateSent: String? = null,
|
||||
dateSentFull: String? = null,
|
||||
duration: String? = null,
|
||||
): MediaInfo = MediaInfo(
|
||||
filename = "a video file.mp4",
|
||||
caption = caption,
|
||||
@@ -64,6 +67,7 @@ fun aVideoMediaInfo(
|
||||
dateSent = dateSent,
|
||||
dateSentFull = dateSentFull,
|
||||
waveform = null,
|
||||
duration = duration,
|
||||
)
|
||||
|
||||
fun aPdfMediaInfo(
|
||||
@@ -84,6 +88,7 @@ fun aPdfMediaInfo(
|
||||
dateSent = dateSent,
|
||||
dateSentFull = dateSentFull,
|
||||
waveform = null,
|
||||
duration = null,
|
||||
)
|
||||
|
||||
fun anApkMediaInfo(
|
||||
@@ -103,6 +108,7 @@ fun anApkMediaInfo(
|
||||
dateSent = dateSent,
|
||||
dateSentFull = dateSentFull,
|
||||
waveform = null,
|
||||
duration = null,
|
||||
)
|
||||
|
||||
fun anAudioMediaInfo(
|
||||
@@ -112,6 +118,7 @@ fun anAudioMediaInfo(
|
||||
dateSent: String? = null,
|
||||
dateSentFull: String? = null,
|
||||
waveForm: List<Float>? = null,
|
||||
duration: String? = null,
|
||||
): MediaInfo = MediaInfo(
|
||||
filename = filename,
|
||||
caption = caption,
|
||||
@@ -124,6 +131,7 @@ fun anAudioMediaInfo(
|
||||
dateSent = dateSent,
|
||||
dateSentFull = dateSentFull,
|
||||
waveform = waveForm,
|
||||
duration = duration,
|
||||
)
|
||||
|
||||
fun aVoiceMediaInfo(
|
||||
@@ -133,6 +141,7 @@ fun aVoiceMediaInfo(
|
||||
dateSent: String? = null,
|
||||
dateSentFull: String? = null,
|
||||
waveForm: List<Float>? = null,
|
||||
duration: String? = null,
|
||||
): MediaInfo = MediaInfo(
|
||||
filename = filename,
|
||||
caption = caption,
|
||||
@@ -145,4 +154,5 @@ fun aVoiceMediaInfo(
|
||||
dateSent = dateSent,
|
||||
dateSentFull = dateSentFull,
|
||||
waveform = waveForm,
|
||||
duration = duration,
|
||||
)
|
||||
|
||||
@@ -56,6 +56,7 @@ class DefaultMediaViewerEntryPoint @Inject constructor() : MediaViewerEntryPoint
|
||||
dateSent = null,
|
||||
dateSentFull = null,
|
||||
waveform = null,
|
||||
duration = null,
|
||||
),
|
||||
mediaSource = MediaSource(url = avatarUrl),
|
||||
thumbnailSource = null,
|
||||
|
||||
@@ -102,6 +102,7 @@ class EventItemFactory @Inject constructor(
|
||||
dateSent = dateSent,
|
||||
dateSentFull = dateSentFull,
|
||||
waveform = null,
|
||||
duration = null,
|
||||
),
|
||||
mediaSource = type.source,
|
||||
)
|
||||
@@ -120,6 +121,7 @@ class EventItemFactory @Inject constructor(
|
||||
dateSent = dateSent,
|
||||
dateSentFull = dateSentFull,
|
||||
waveform = null,
|
||||
duration = null,
|
||||
),
|
||||
mediaSource = type.source,
|
||||
)
|
||||
@@ -138,6 +140,7 @@ class EventItemFactory @Inject constructor(
|
||||
dateSent = dateSent,
|
||||
dateSentFull = dateSentFull,
|
||||
waveform = null,
|
||||
duration = null,
|
||||
),
|
||||
mediaSource = type.source,
|
||||
thumbnailSource = null,
|
||||
@@ -157,6 +160,7 @@ class EventItemFactory @Inject constructor(
|
||||
dateSent = dateSent,
|
||||
dateSentFull = dateSentFull,
|
||||
waveform = null,
|
||||
duration = null,
|
||||
),
|
||||
mediaSource = type.source,
|
||||
thumbnailSource = null,
|
||||
@@ -176,10 +180,10 @@ class EventItemFactory @Inject constructor(
|
||||
dateSent = dateSent,
|
||||
dateSentFull = dateSentFull,
|
||||
waveform = null,
|
||||
duration = type.info?.duration?.inWholeMilliseconds?.toHumanReadableDuration(),
|
||||
),
|
||||
mediaSource = type.source,
|
||||
thumbnailSource = type.info?.thumbnailSource,
|
||||
duration = type.info?.duration?.inWholeMilliseconds?.toHumanReadableDuration(),
|
||||
)
|
||||
is VoiceMessageType -> MediaItem.Voice(
|
||||
id = currentTimelineItem.uniqueId,
|
||||
@@ -196,10 +200,9 @@ class EventItemFactory @Inject constructor(
|
||||
dateSent = dateSent,
|
||||
dateSentFull = dateSentFull,
|
||||
waveform = type.details?.waveform.orEmpty(),
|
||||
duration = type.info?.duration?.inWholeMilliseconds?.toHumanReadableDuration(),
|
||||
),
|
||||
mediaSource = type.source,
|
||||
duration = type.info?.duration?.inWholeMilliseconds?.toHumanReadableDuration(),
|
||||
waveform = type.details?.waveform ?: persistentListOf(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.ui.media.MediaRequestData
|
||||
import io.element.android.libraries.mediaviewer.api.MediaInfo
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
sealed interface MediaItem {
|
||||
data class DateSeparator(
|
||||
@@ -46,7 +45,6 @@ sealed interface MediaItem {
|
||||
val mediaInfo: MediaInfo,
|
||||
val mediaSource: MediaSource,
|
||||
val thumbnailSource: MediaSource?,
|
||||
val duration: String?,
|
||||
) : Event {
|
||||
val thumbnailMediaRequestData: MediaRequestData
|
||||
get() = MediaRequestData(thumbnailSource ?: mediaSource, MediaRequestData.Kind.Thumbnail(100))
|
||||
@@ -64,8 +62,6 @@ sealed interface MediaItem {
|
||||
val eventId: EventId?,
|
||||
val mediaInfo: MediaInfo,
|
||||
val mediaSource: MediaSource,
|
||||
val duration: String?,
|
||||
val waveform: ImmutableList<Float>,
|
||||
) : Event
|
||||
|
||||
data class File(
|
||||
|
||||
@@ -16,7 +16,6 @@ import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
class SingleMediaGalleryDataSource(
|
||||
@@ -32,77 +31,54 @@ class SingleMediaGalleryDataSource(
|
||||
fun createFrom(params: MediaViewerEntryPoint.Params) = SingleMediaGalleryDataSource(
|
||||
data = when {
|
||||
params.mediaInfo.mimeType.isMimeTypeImage() -> {
|
||||
GroupedMediaItems(
|
||||
imageAndVideoItems = persistentListOf(
|
||||
MediaItem.Image(
|
||||
id = UniqueId("dummy"),
|
||||
eventId = params.eventId,
|
||||
mediaInfo = params.mediaInfo,
|
||||
mediaSource = params.mediaSource,
|
||||
thumbnailSource = params.thumbnailSource,
|
||||
)
|
||||
),
|
||||
fileItems = persistentListOf(),
|
||||
MediaItem.Image(
|
||||
id = UniqueId("dummy"),
|
||||
eventId = params.eventId,
|
||||
mediaInfo = params.mediaInfo,
|
||||
mediaSource = params.mediaSource,
|
||||
thumbnailSource = params.thumbnailSource,
|
||||
)
|
||||
}
|
||||
params.mediaInfo.mimeType.isMimeTypeVideo() -> {
|
||||
GroupedMediaItems(
|
||||
imageAndVideoItems = persistentListOf(
|
||||
MediaItem.Video(
|
||||
id = UniqueId("dummy"),
|
||||
eventId = params.eventId,
|
||||
mediaInfo = params.mediaInfo,
|
||||
mediaSource = params.mediaSource,
|
||||
thumbnailSource = params.thumbnailSource,
|
||||
duration = "TODO", // TODO Duration
|
||||
)
|
||||
),
|
||||
fileItems = persistentListOf(),
|
||||
MediaItem.Video(
|
||||
id = UniqueId("dummy"),
|
||||
eventId = params.eventId,
|
||||
mediaInfo = params.mediaInfo,
|
||||
mediaSource = params.mediaSource,
|
||||
thumbnailSource = params.thumbnailSource,
|
||||
)
|
||||
}
|
||||
params.mediaInfo.mimeType.isMimeTypeAudio() -> {
|
||||
if (params.mediaInfo.waveform == null) {
|
||||
GroupedMediaItems(
|
||||
imageAndVideoItems = persistentListOf(
|
||||
MediaItem.Audio(
|
||||
id = UniqueId("dummy"),
|
||||
eventId = params.eventId,
|
||||
mediaInfo = params.mediaInfo,
|
||||
mediaSource = params.mediaSource,
|
||||
)
|
||||
),
|
||||
fileItems = persistentListOf(),
|
||||
MediaItem.Audio(
|
||||
id = UniqueId("dummy"),
|
||||
eventId = params.eventId,
|
||||
mediaInfo = params.mediaInfo,
|
||||
mediaSource = params.mediaSource,
|
||||
)
|
||||
} else {
|
||||
GroupedMediaItems(
|
||||
imageAndVideoItems = persistentListOf(
|
||||
MediaItem.Voice(
|
||||
id = UniqueId("dummy"),
|
||||
eventId = params.eventId,
|
||||
mediaInfo = params.mediaInfo,
|
||||
mediaSource = params.mediaSource,
|
||||
duration = "TODO", // TODO Duration
|
||||
waveform = params.mediaInfo.waveform.orEmpty().toImmutableList(),
|
||||
)
|
||||
),
|
||||
fileItems = persistentListOf(),
|
||||
MediaItem.Voice(
|
||||
id = UniqueId("dummy"),
|
||||
eventId = params.eventId,
|
||||
mediaInfo = params.mediaInfo,
|
||||
mediaSource = params.mediaSource,
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Always use imageAndVideoItems, in Single mode, this is the data that will be used
|
||||
GroupedMediaItems(
|
||||
imageAndVideoItems = persistentListOf(
|
||||
MediaItem.File(
|
||||
id = UniqueId("dummy"),
|
||||
eventId = params.eventId,
|
||||
mediaInfo = params.mediaInfo,
|
||||
mediaSource = params.mediaSource,
|
||||
)
|
||||
),
|
||||
fileItems = persistentListOf(),
|
||||
MediaItem.File(
|
||||
id = UniqueId("dummy"),
|
||||
eventId = params.eventId,
|
||||
mediaInfo = params.mediaInfo,
|
||||
mediaSource = params.mediaSource,
|
||||
)
|
||||
}
|
||||
}.let { mediaItem ->
|
||||
GroupedMediaItems(
|
||||
// Always use imageAndVideoItems, in Single mode, this is the data that will be used
|
||||
imageAndVideoItems = persistentListOf(mediaItem),
|
||||
fileItems = persistentListOf(),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,9 +31,10 @@ fun aMediaItemVideo(
|
||||
return MediaItem.Video(
|
||||
id = id,
|
||||
eventId = null,
|
||||
mediaInfo = aVideoMediaInfo(),
|
||||
mediaInfo = aVideoMediaInfo(
|
||||
duration = duration
|
||||
),
|
||||
mediaSource = mediaSource,
|
||||
thumbnailSource = null,
|
||||
duration = duration,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import io.element.android.libraries.mediaviewer.api.aVoiceMediaInfo
|
||||
import io.element.android.libraries.mediaviewer.impl.gallery.MediaItem
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
class MediaItemVoiceProvider : PreviewParameterProvider<MediaItem.Voice> {
|
||||
override val values: Sequence<MediaItem.Voice>
|
||||
@@ -46,9 +45,9 @@ fun aMediaItemVoice(
|
||||
mediaInfo = aVoiceMediaInfo(
|
||||
filename = filename,
|
||||
caption = caption,
|
||||
duration = duration,
|
||||
waveForm = waveform,
|
||||
),
|
||||
mediaSource = MediaSource(""),
|
||||
duration = duration,
|
||||
waveform = waveform.toImmutableList(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -101,10 +101,10 @@ private fun VideoInfoRow(
|
||||
imageVector = CompoundIcons.VideoCallSolid(),
|
||||
contentDescription = null
|
||||
)
|
||||
if (video.duration != null) {
|
||||
video.mediaInfo.duration?.let { duration ->
|
||||
Spacer(Modifier.weight(1f))
|
||||
Text(
|
||||
text = video.duration,
|
||||
text = duration,
|
||||
style = ElementTheme.typography.fontBodySmMedium,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
|
||||
@@ -115,7 +115,7 @@ private fun VoiceInfoRow(
|
||||
}
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(
|
||||
text = if (state.progress > 0f) state.time else voice.duration ?: state.time,
|
||||
text = if (state.progress > 0f) state.time else voice.mediaInfo.duration ?: state.time,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
style = ElementTheme.typography.fontBodyMdMedium,
|
||||
maxLines = 1,
|
||||
@@ -128,7 +128,7 @@ private fun VoiceInfoRow(
|
||||
.height(34.dp),
|
||||
showCursor = state.showCursor,
|
||||
playbackProgress = state.progress,
|
||||
waveform = voice.waveform.toPersistentList(),
|
||||
waveform = voice.mediaInfo.waveform.orEmpty().toPersistentList(),
|
||||
onSeek = {
|
||||
state.eventSink(VoiceMessageEvents.Seek(it))
|
||||
},
|
||||
|
||||
@@ -48,6 +48,7 @@ class AndroidLocalMediaFactory @Inject constructor(
|
||||
dateSent = mediaInfo.dateSent,
|
||||
dateSentFull = mediaInfo.dateSentFull,
|
||||
waveform = mediaInfo.waveform,
|
||||
duration = mediaInfo.duration,
|
||||
)
|
||||
|
||||
override fun createFromUri(
|
||||
@@ -67,6 +68,7 @@ class AndroidLocalMediaFactory @Inject constructor(
|
||||
dateSent = null,
|
||||
dateSentFull = null,
|
||||
waveform = null,
|
||||
duration = null,
|
||||
)
|
||||
|
||||
private fun createFromUri(
|
||||
@@ -81,6 +83,7 @@ class AndroidLocalMediaFactory @Inject constructor(
|
||||
dateSent: String?,
|
||||
dateSentFull: String?,
|
||||
waveform: List<Float>?,
|
||||
duration: String?,
|
||||
): LocalMedia {
|
||||
val resolvedMimeType = mimeType ?: context.getMimeType(uri) ?: MimeTypes.OctetStream
|
||||
val fileName = name ?: context.getFileName(uri) ?: ""
|
||||
@@ -100,6 +103,7 @@ class AndroidLocalMediaFactory @Inject constructor(
|
||||
dateSent = dateSent,
|
||||
dateSentFull = dateSentFull,
|
||||
waveform = waveform,
|
||||
duration = duration,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user