Skip to content

Commit 2a3fd99

Browse files
authored
Merge pull request #4031 from element-hq/feature/bma/fileListAudioPlayer
Render audio file in the files list and improve media viewer for audio/voice files
2 parents 29eca97 + b236389 commit 2a3fd99

File tree

58 files changed

+1034
-135
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1034
-135
lines changed

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
4747
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
4848
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
4949
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
50+
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
5051
import io.element.android.features.poll.api.create.CreatePollEntryPoint
5152
import io.element.android.features.poll.api.create.CreatePollMode
5253
import io.element.android.libraries.architecture.BackstackWithOverlayBox
@@ -447,6 +448,7 @@ class MessagesFlowNode @AssistedInject constructor(
447448
timestamp = event.sentTimeMillis,
448449
mode = DateFormatterMode.Full,
449450
),
451+
waveform = (content as? TimelineItemVoiceContent)?.waveform,
450452
),
451453
mediaSource = mediaSource,
452454
thumbnailSource = thumbnailSource,

libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/WaveformPlaybackView.kt

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ internal fun WaveformPlaybackViewPreview() = ElementPreview {
189189
showCursor = false,
190190
playbackProgress = 0.5f,
191191
onSeek = {},
192-
waveform = persistentListOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f, 8f, 7f, 6f, 5f, 4f, 3f, 2f, 1f, 0f),
192+
waveform = aWaveForm().toPersistentList(),
193193
)
194194
WaveformPlaybackView(
195195
modifier = Modifier.height(34.dp),
@@ -219,3 +219,45 @@ private fun ImmutableList<Float>.normalisedData(maxSamplesCount: Int): Immutable
219219

220220
return result.toPersistentList()
221221
}
222+
223+
fun aWaveForm(): List<Float> {
224+
return listOf(
225+
0.000f,
226+
0.000f,
227+
0.000f,
228+
0.003f,
229+
0.354f,
230+
0.353f,
231+
0.365f,
232+
0.790f,
233+
0.787f,
234+
0.167f,
235+
0.333f,
236+
0.975f,
237+
0.000f,
238+
0.102f,
239+
0.003f,
240+
0.531f,
241+
0.584f,
242+
0.317f,
243+
0.140f,
244+
0.475f,
245+
0.496f,
246+
0.561f,
247+
0.042f,
248+
0.263f,
249+
0.169f,
250+
0.829f,
251+
0.349f,
252+
0.010f,
253+
0.000f,
254+
0.000f,
255+
1.000f,
256+
0.334f,
257+
0.321f,
258+
0.011f,
259+
0.000f,
260+
0.000f,
261+
0.003f,
262+
)
263+
}

libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ data class MediaInfo(
2424
val senderAvatar: String?,
2525
val dateSent: String?,
2626
val dateSentFull: String?,
27+
val waveform: List<Float>?,
2728
) : Parcelable
2829

2930
fun anImageMediaInfo(
@@ -43,6 +44,7 @@ fun anImageMediaInfo(
4344
senderAvatar = null,
4445
dateSent = dateSent,
4546
dateSentFull = dateSentFull,
47+
waveform = null,
4648
)
4749

4850
fun aVideoMediaInfo(
@@ -61,6 +63,7 @@ fun aVideoMediaInfo(
6163
senderAvatar = null,
6264
dateSent = dateSent,
6365
dateSentFull = dateSentFull,
66+
waveform = null,
6467
)
6568

6669
fun aPdfMediaInfo(
@@ -80,6 +83,7 @@ fun aPdfMediaInfo(
8083
senderAvatar = null,
8184
dateSent = dateSent,
8285
dateSentFull = dateSentFull,
86+
waveform = null,
8387
)
8488

8589
fun anApkMediaInfo(
@@ -98,15 +102,19 @@ fun anApkMediaInfo(
98102
senderAvatar = null,
99103
dateSent = dateSent,
100104
dateSentFull = dateSentFull,
105+
waveform = null,
101106
)
102107

103108
fun anAudioMediaInfo(
109+
filename: String = "an audio file.mp3",
110+
caption: String? = null,
104111
senderName: String? = null,
105112
dateSent: String? = null,
106113
dateSentFull: String? = null,
114+
waveForm: List<Float>? = null,
107115
): MediaInfo = MediaInfo(
108-
filename = "an audio file.mp3",
109-
caption = null,
116+
filename = filename,
117+
caption = caption,
110118
mimeType = MimeTypes.Mp3,
111119
formattedFileSize = "7MB",
112120
fileExtension = "mp3",
@@ -115,4 +123,5 @@ fun anAudioMediaInfo(
115123
senderAvatar = null,
116124
dateSent = dateSent,
117125
dateSentFull = dateSentFull,
126+
waveform = waveForm,
118127
)

libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class DefaultMediaViewerEntryPoint @Inject constructor() : MediaViewerEntryPoint
5454
senderAvatar = null,
5555
dateSent = null,
5656
dateSentFull = null,
57+
waveform = null,
5758
),
5859
mediaSource = MediaSource(url = avatarUrl),
5960
thumbnailSource = null,

libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/EventItemFactory.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class EventItemFactory @Inject constructor(
8686
Timber.w("Should not happen: ${content.type}")
8787
null
8888
}
89-
is AudioMessageType -> MediaItem.File(
89+
is AudioMessageType -> MediaItem.Audio(
9090
id = currentTimelineItem.uniqueId,
9191
eventId = currentTimelineItem.eventId,
9292
mediaInfo = MediaInfo(
@@ -100,8 +100,11 @@ class EventItemFactory @Inject constructor(
100100
senderAvatar = event.senderProfile.getAvatarUrl(),
101101
dateSent = dateSent,
102102
dateSentFull = dateSentFull,
103+
waveform = null,
103104
),
104105
mediaSource = type.source,
106+
duration = type.info?.duration?.inWholeMilliseconds?.toHumanReadableDuration(),
107+
waveform = null,
105108
)
106109
is FileMessageType -> MediaItem.File(
107110
id = currentTimelineItem.uniqueId,
@@ -117,6 +120,7 @@ class EventItemFactory @Inject constructor(
117120
senderAvatar = event.senderProfile.getAvatarUrl(),
118121
dateSent = dateSent,
119122
dateSentFull = dateSentFull,
123+
waveform = null,
120124
),
121125
mediaSource = type.source,
122126
)
@@ -134,6 +138,7 @@ class EventItemFactory @Inject constructor(
134138
senderAvatar = event.senderProfile.getAvatarUrl(),
135139
dateSent = dateSent,
136140
dateSentFull = dateSentFull,
141+
waveform = null,
137142
),
138143
mediaSource = type.source,
139144
thumbnailSource = null,
@@ -152,6 +157,7 @@ class EventItemFactory @Inject constructor(
152157
senderAvatar = event.senderProfile.getAvatarUrl(),
153158
dateSent = dateSent,
154159
dateSentFull = dateSentFull,
160+
waveform = null,
155161
),
156162
mediaSource = type.source,
157163
thumbnailSource = null,
@@ -170,12 +176,13 @@ class EventItemFactory @Inject constructor(
170176
senderAvatar = event.senderProfile.getAvatarUrl(),
171177
dateSent = dateSent,
172178
dateSentFull = dateSentFull,
179+
waveform = null,
173180
),
174181
mediaSource = type.source,
175182
thumbnailSource = type.info?.thumbnailSource,
176183
duration = type.info?.duration?.inWholeMilliseconds?.toHumanReadableDuration(),
177184
)
178-
is VoiceMessageType -> MediaItem.File(
185+
is VoiceMessageType -> MediaItem.Audio(
179186
id = currentTimelineItem.uniqueId,
180187
eventId = currentTimelineItem.eventId,
181188
mediaInfo = MediaInfo(
@@ -189,8 +196,11 @@ class EventItemFactory @Inject constructor(
189196
senderAvatar = event.senderProfile.getAvatarUrl(),
190197
dateSent = dateSent,
191198
dateSentFull = dateSentFull,
199+
waveform = type.details?.waveform.orEmpty(),
192200
),
193201
mediaSource = type.source,
202+
duration = type.info?.duration?.inWholeMilliseconds?.toHumanReadableDuration(),
203+
waveform = type.details?.waveform,
194204
)
195205
}
196206
}

libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ class MediaGalleryPresenter @AssistedInject constructor(
135135
thumbnailSource = when (event.mediaItem) {
136136
is MediaItem.Image -> event.mediaItem.thumbnailSource ?: event.mediaItem.mediaSource
137137
is MediaItem.Video -> event.mediaItem.thumbnailSource ?: event.mediaItem.mediaSource
138+
is MediaItem.Audio -> null
138139
is MediaItem.File -> null
139140
},
140141
)

libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryStateProvider.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ package io.element.android.libraries.mediaviewer.impl.gallery
99

1010
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
1111
import io.element.android.libraries.architecture.AsyncData
12+
import io.element.android.libraries.designsystem.components.media.aWaveForm
1213
import io.element.android.libraries.matrix.api.core.UniqueId
1314
import io.element.android.libraries.mediaviewer.impl.details.MediaBottomSheetState
1415
import io.element.android.libraries.mediaviewer.impl.details.aMediaDetailsBottomSheetState
16+
import io.element.android.libraries.mediaviewer.impl.gallery.ui.aMediaItemAudio
1517
import io.element.android.libraries.mediaviewer.impl.gallery.ui.aMediaItemDateSeparator
1618
import io.element.android.libraries.mediaviewer.impl.gallery.ui.aMediaItemFile
1719
import io.element.android.libraries.mediaviewer.impl.gallery.ui.aMediaItemImage
@@ -62,7 +64,11 @@ open class MediaGalleryStateProvider : PreviewParameterProvider<MediaGalleryStat
6264
formattedDate = "September 2004",
6365
),
6466
aMediaItemFile(id = UniqueId("3")),
65-
aMediaItemFile(id = UniqueId("4")),
67+
aMediaItemAudio(id = UniqueId("4")),
68+
aMediaItemAudio(
69+
id = UniqueId("5"),
70+
waveform = aWaveForm(),
71+
),
6672
aMediaItemLoadingIndicator(),
6773
).toImmutableList()
6874
)

libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryView.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import io.element.android.libraries.mediaviewer.impl.R
5959
import io.element.android.libraries.mediaviewer.impl.details.MediaBottomSheetState
6060
import io.element.android.libraries.mediaviewer.impl.details.MediaDeleteConfirmationBottomSheet
6161
import io.element.android.libraries.mediaviewer.impl.details.MediaDetailsBottomSheet
62+
import io.element.android.libraries.mediaviewer.impl.gallery.ui.AudioItemView
6263
import io.element.android.libraries.mediaviewer.impl.gallery.ui.DateItemView
6364
import io.element.android.libraries.mediaviewer.impl.gallery.ui.FileItemView
6465
import io.element.android.libraries.mediaviewer.impl.gallery.ui.ImageItemView
@@ -257,6 +258,13 @@ private fun MediaGalleryFilesList(
257258
onDownloadClick = { eventSink(MediaGalleryEvents.SaveOnDisk(item)) },
258259
onInfoClick = { eventSink(MediaGalleryEvents.OpenInfo(item)) },
259260
)
261+
is MediaItem.Audio -> AudioItemView(
262+
item,
263+
onClick = { onItemClick(item) },
264+
onShareClick = { eventSink(MediaGalleryEvents.Share(item)) },
265+
onDownloadClick = { eventSink(MediaGalleryEvents.SaveOnDisk(item)) },
266+
onInfoClick = { eventSink(MediaGalleryEvents.OpenInfo(item)) },
267+
)
260268
is MediaItem.DateSeparator -> DateItemView(item)
261269
is MediaItem.Image,
262270
is MediaItem.Video -> {
@@ -312,6 +320,9 @@ private fun MediaGalleryImageGrid(
312320
is MediaItem.DateSeparator -> {
313321
DateItemView(item)
314322
}
323+
is MediaItem.Audio -> {
324+
// Should not happen
325+
}
315326
is MediaItem.File -> {
316327
// Should not happen
317328
}

libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaItem.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.media.MediaSource
1313
import io.element.android.libraries.matrix.api.timeline.Timeline
1414
import io.element.android.libraries.matrix.ui.media.MediaRequestData
1515
import io.element.android.libraries.mediaviewer.api.MediaInfo
16+
import kotlinx.collections.immutable.ImmutableList
1617

1718
sealed interface MediaItem {
1819
data class DateSeparator(
@@ -51,6 +52,15 @@ sealed interface MediaItem {
5152
get() = MediaRequestData(thumbnailSource ?: mediaSource, MediaRequestData.Kind.Thumbnail(100))
5253
}
5354

55+
data class Audio(
56+
val id: UniqueId,
57+
val eventId: EventId?,
58+
val mediaInfo: MediaInfo,
59+
val mediaSource: MediaSource,
60+
val duration: String?,
61+
val waveform: ImmutableList<Float>?,
62+
) : Event
63+
5464
data class File(
5565
val id: UniqueId,
5666
val eventId: EventId?,
@@ -66,6 +76,7 @@ fun MediaItem.id(): UniqueId {
6676
is MediaItem.Image -> id
6777
is MediaItem.Video -> id
6878
is MediaItem.File -> id
79+
is MediaItem.Audio -> id
6980
}
7081
}
7182

@@ -74,6 +85,7 @@ fun MediaItem.Event.eventId(): EventId? {
7485
is MediaItem.Image -> eventId
7586
is MediaItem.Video -> eventId
7687
is MediaItem.File -> eventId
88+
is MediaItem.Audio -> eventId
7789
}
7890
}
7991

@@ -82,6 +94,7 @@ fun MediaItem.Event.mediaInfo(): MediaInfo {
8294
is MediaItem.Image -> mediaInfo
8395
is MediaItem.Video -> mediaInfo
8496
is MediaItem.File -> mediaInfo
97+
is MediaItem.Audio -> mediaInfo
8598
}
8699
}
87100

@@ -90,6 +103,7 @@ fun MediaItem.Event.mediaSource(): MediaSource {
90103
is MediaItem.Image -> mediaSource
91104
is MediaItem.Video -> mediaSource
92105
is MediaItem.File -> mediaSource
106+
is MediaItem.Audio -> mediaSource
93107
}
94108
}
95109

@@ -98,5 +112,6 @@ fun MediaItem.Event.thumbnailSource(): MediaSource? {
98112
is MediaItem.Image -> thumbnailSource
99113
is MediaItem.Video -> thumbnailSource
100114
is MediaItem.File -> null
115+
is MediaItem.Audio -> null
101116
}
102117
}

libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaItemsPostProcessor.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class MediaItemsPostProcessor @Inject constructor() {
5656
is MediaItem.Video -> {
5757
imageAndVideoItemsSubList.add(0, item)
5858
}
59+
is MediaItem.Audio,
5960
is MediaItem.File -> {
6061
fileItemsSublist.add(0, item)
6162
}

0 commit comments

Comments
 (0)