Merge pull request #1903 from vector-im/feature/bma/kotlinDuration

Kotlin duration and MimeTypes
This commit is contained in:
Benoit Marty 2023-11-28 09:45:31 +01:00 committed by GitHub
commit a62d1b297f
31 changed files with 94 additions and 69 deletions

View file

@ -47,8 +47,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageT
import io.element.android.libraries.matrix.ui.messages.toHtmlDocument
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import java.time.Duration
import javax.inject.Inject
import kotlin.time.Duration
class TimelineItemContentMessageFactory @Inject constructor(
private val fileSizeFormatter: FileSizeFormatter,
@ -104,7 +104,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
width = messageType.info?.width?.toInt(),
height = messageType.info?.height?.toInt(),
duration = messageType.info?.duration?.toMillis() ?: 0L,
duration = messageType.info?.duration ?: Duration.ZERO,
blurHash = messageType.info?.blurhash,
aspectRatio = aspectRatio,
formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),

View file

@ -18,7 +18,7 @@ package io.element.android.features.messages.impl.timeline.model.event
import io.element.android.features.messages.impl.media.helper.formatFileExtensionAndSize
import io.element.android.libraries.matrix.api.media.MediaSource
import java.time.Duration
import kotlin.time.Duration
data class TimelineItemAudioContent(
val body: String,

View file

@ -19,7 +19,7 @@ package io.element.android.features.messages.impl.timeline.model.event
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.media.MediaSource
import java.time.Duration
import kotlin.time.Duration.Companion.milliseconds
open class TimelineItemAudioContentProvider : PreviewParameterProvider<TimelineItemAudioContent> {
override val values: Sequence<TimelineItemAudioContent>
@ -35,6 +35,6 @@ fun aTimelineItemAudioContent(fileName: String = "A sound.mp3") = TimelineItemAu
mimeType = MimeTypes.Pdf,
formattedFileSize = "100kB",
fileExtension = "mp3",
duration = Duration.ofMillis(100),
duration = 100.milliseconds,
mediaSource = MediaSource(""),
)

View file

@ -17,10 +17,11 @@
package io.element.android.features.messages.impl.timeline.model.event
import io.element.android.libraries.matrix.api.media.MediaSource
import kotlin.time.Duration
data class TimelineItemVideoContent(
val body: String,
val duration: Long,
val duration: Duration,
val videoSource: MediaSource,
val thumbnailSource: MediaSource?,
val aspectRatio: Float?,

View file

@ -20,6 +20,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH
import kotlin.time.Duration.Companion.milliseconds
open class TimelineItemVideoContentProvider : PreviewParameterProvider<TimelineItemVideoContent> {
override val values: Sequence<TimelineItemVideoContent>
@ -35,7 +36,7 @@ fun aTimelineItemVideoContent() = TimelineItemVideoContent(
thumbnailSource = null,
blurHash = A_BLUR_HASH,
aspectRatio = 0.5f,
duration = 100,
duration = 100.milliseconds,
videoSource = MediaSource(""),
height = 300,
width = 150,

View file

@ -19,7 +19,7 @@ package io.element.android.features.messages.impl.timeline.model.event
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.media.MediaSource
import kotlinx.collections.immutable.ImmutableList
import java.time.Duration
import kotlin.time.Duration
data class TimelineItemVoiceContent(
val eventId: EventId?,

View file

@ -21,21 +21,23 @@ import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.media.MediaSource
import kotlinx.collections.immutable.toPersistentList
import java.time.Duration
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.minutes
open class TimelineItemVoiceContentProvider : PreviewParameterProvider<TimelineItemVoiceContent> {
override val values: Sequence<TimelineItemVoiceContent>
get() = sequenceOf(
aTimelineItemVoiceContent(
durationMs = 1,
duration = 1.milliseconds,
waveform = listOf(),
),
aTimelineItemVoiceContent(
durationMs = 10_000,
duration = 10_000.milliseconds,
waveform = listOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f, 8f, 7f, 6f, 5f, 4f, 3f, 2f, 1f, 0f),
),
aTimelineItemVoiceContent(
durationMs = 1_800_000, // 30 minutes
duration = 30.minutes,
waveform = List(1024) { it / 1024f },
),
)
@ -44,14 +46,14 @@ open class TimelineItemVoiceContentProvider : PreviewParameterProvider<TimelineI
fun aTimelineItemVoiceContent(
eventId: String? = "\$anEventId",
body: String = "body doesn't really matter for a voice message",
durationMs: Long = 61_000,
duration: Duration = 61_000.milliseconds,
contentUri: String = "mxc://matrix.org/1234567890abcdefg",
mimeType: String = MimeTypes.Ogg,
waveform: List<Float> = listOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f, 8f, 7f, 6f, 5f, 4f, 3f, 2f, 1f, 0f),
) = TimelineItemVoiceContent(
eventId = eventId?.let { EventId(it) },
body = body,
duration = Duration.ofMillis(durationMs),
duration = duration,
mediaSource = MediaSource(contentUri),
mimeType = mimeType,
waveform = waveform.toPersistentList(),

View file

@ -16,6 +16,7 @@
package io.element.android.features.messages.impl.voicemessages.composer
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.mediaplayer.api.MediaPlayer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@ -41,7 +42,7 @@ class VoiceMessageComposerPlayer @Inject constructor(
private val coroutineScope: CoroutineScope,
) {
companion object {
const val MIME_TYPE = "audio/ogg"
const val MIME_TYPE = MimeTypes.Ogg
}
private var mediaPath: String? = null
@ -201,6 +202,7 @@ class VoiceMessageComposerPlayer @Inject constructor(
progress = 0f,
)
}
/**
* Whether this player is currently playing.
*/
@ -212,7 +214,6 @@ class VoiceMessageComposerPlayer @Inject constructor(
val isStopped get() = this.playState == PlayState.Stopped
}
enum class PlayState {
/**
* The player is stopped, i.e. it has just been initialised.

View file

@ -17,6 +17,7 @@
package io.element.android.features.messages.impl.voicemessages.timeline
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.media.MediaSource
@ -196,7 +197,7 @@ class DefaultVoiceMessagePlayer(
mediaPlayer.setMedia(
uri = mediaFile.path,
mediaId = eventId.value,
mimeType = "audio/ogg", // Files in the voice cache have no extension so we need to set the mime type manually.
mimeType = MimeTypes.Ogg, // Files in the voice cache have no extension so we need to set the mime type manually.
startPositionMs = if (state.isEnded) 0L else state.currentPosition,
)
}

View file

@ -98,7 +98,7 @@ class VoiceMessagePresenter @AssistedInject constructor(
}
}
val duration by remember {
derivedStateOf { playerState.duration ?: content.duration.toMillis() }
derivedStateOf { playerState.duration ?: content.duration.inWholeMilliseconds }
}
val progress by remember {
derivedStateOf {

View file

@ -264,7 +264,7 @@ class MessagesPresenterTest {
val mediaMessage = aMessageEvent(
content = TimelineItemVideoContent(
body = "video.mp4",
duration = 10L,
duration = 10.milliseconds,
videoSource = MediaSource(AN_AVATAR_URL),
thumbnailSource = MediaSource(AN_AVATAR_URL),
mimeType = MimeTypes.Mp4,

View file

@ -58,8 +58,8 @@ import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.test.runTest
import org.junit.Test
import java.time.Duration
import java.time.Duration.ofMinutes
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
class TimelineItemContentMessageFactoryTest {
@ -140,14 +140,14 @@ class TimelineItemContentMessageFactoryTest {
)
val expected = TimelineItemVideoContent(
body = "body",
duration = 0,
duration = Duration.ZERO,
videoSource = MediaSource(url = "url", json = null),
thumbnailSource = null,
aspectRatio = null,
blurHash = null,
height = null,
width = null,
mimeType = "application/octet-stream",
mimeType = MimeTypes.OctetStream,
formattedFileSize = "0 Bytes",
fileExtension = "",
)
@ -163,7 +163,7 @@ class TimelineItemContentMessageFactoryTest {
body = "body.mp4",
source = MediaSource("url"),
info = VideoInfo(
duration = ofMinutes(1),
duration = 1.minutes,
height = 100,
width = 300,
mimetype = MimeTypes.Mp4,
@ -184,7 +184,7 @@ class TimelineItemContentMessageFactoryTest {
)
val expected = TimelineItemVideoContent(
body = "body.mp4",
duration = 60_000,
duration = 1.minutes,
videoSource = MediaSource(url = "url", json = null),
thumbnailSource = MediaSource("url_thumbnail"),
aspectRatio = 3f,
@ -210,7 +210,7 @@ class TimelineItemContentMessageFactoryTest {
body = "body",
duration = Duration.ZERO,
mediaSource = MediaSource(url = "url", json = null),
mimeType = "application/octet-stream",
mimeType = MimeTypes.OctetStream,
formattedFileSize = "0 Bytes",
fileExtension = "",
)
@ -226,7 +226,7 @@ class TimelineItemContentMessageFactoryTest {
body = "body.mp3",
source = MediaSource("url"),
info = AudioInfo(
duration = ofMinutes(1),
duration = 1.minutes,
size = 123L,
mimetype = MimeTypes.Mp3,
)
@ -237,7 +237,7 @@ class TimelineItemContentMessageFactoryTest {
)
val expected = TimelineItemAudioContent(
body = "body.mp3",
duration = ofMinutes(1),
duration = 1.minutes,
mediaSource = MediaSource(url = "url", json = null),
mimeType = MimeTypes.Mp3,
formattedFileSize = "123 Bytes",
@ -259,7 +259,7 @@ class TimelineItemContentMessageFactoryTest {
body = "body",
duration = Duration.ZERO,
mediaSource = MediaSource(url = "url", json = null),
mimeType = "application/octet-stream",
mimeType = MimeTypes.OctetStream,
waveform = emptyList<Float>().toImmutableList()
)
assertThat(result).isEqualTo(expected)
@ -274,12 +274,13 @@ class TimelineItemContentMessageFactoryTest {
body = "body.ogg",
source = MediaSource("url"),
info = AudioInfo(
duration = ofMinutes(1),
duration = 1.minutes,
size = 123L,
mimetype = MimeTypes.Ogg,
),
details = AudioDetails(
duration = ofMinutes(1), waveform = listOf(1f, 2f)
duration = 1.minutes,
waveform = listOf(1f, 2f),
),
)
),
@ -289,7 +290,7 @@ class TimelineItemContentMessageFactoryTest {
val expected = TimelineItemVoiceContent(
eventId = AN_EVENT_ID,
body = "body.ogg",
duration = ofMinutes(1),
duration = 1.minutes,
mediaSource = MediaSource(url = "url", json = null),
mimeType = MimeTypes.Ogg,
waveform = listOf(1f, 2f).toImmutableList()
@ -315,7 +316,7 @@ class TimelineItemContentMessageFactoryTest {
body = "body",
duration = Duration.ZERO,
mediaSource = MediaSource(url = "url", json = null),
mimeType = "application/octet-stream",
mimeType = MimeTypes.OctetStream,
formattedFileSize = "0 Bytes",
fileExtension = ""
)
@ -336,7 +337,7 @@ class TimelineItemContentMessageFactoryTest {
thumbnailSource = null,
formattedFileSize = "0 Bytes",
fileExtension = "",
mimeType = "application/octet-stream",
mimeType = MimeTypes.OctetStream,
blurhash = null,
width = null,
height = null,
@ -401,7 +402,7 @@ class TimelineItemContentMessageFactoryTest {
thumbnailSource = null,
formattedFileSize = "0 Bytes",
fileExtension = "",
mimeType = "application/octet-stream"
mimeType = MimeTypes.OctetStream
)
assertThat(result).isEqualTo(expected)
}

View file

@ -17,6 +17,7 @@
package io.element.android.features.messages.impl.voicemessages.timeline
import com.google.common.truth.Truth
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.test.media.FakeMediaLoader
@ -143,7 +144,7 @@ private fun createDefaultVoiceMessageMediaRepo(
url = mxcUri,
json = null
),
mimeType = "audio/ogg",
mimeType = MimeTypes.Ogg,
body = "someBody.ogg"
)

View file

@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.voicemessages.timeline
import app.cash.turbine.TurbineTestContext
import app.cash.turbine.test
import com.google.common.truth.Truth
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.test.AN_EVENT_ID
@ -287,7 +288,7 @@ private fun createDefaultVoiceMessagePlayer(
url = MXC_URI,
json = null
),
mimeType = "audio/ogg",
mimeType = MimeTypes.Ogg,
body = "someBody.ogg"
)

View file

@ -17,6 +17,7 @@
package io.element.android.features.messages.impl.voicemessages.timeline
import com.google.common.truth.Truth
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
@ -37,7 +38,7 @@ class RedactedVoiceMessageManagerTest {
@Test
fun `redacted event - no playing related media`() = runTest {
val mediaPlayer = FakeMediaPlayer().apply {
setMedia(uri = "someUri", mediaId = AN_EVENT_ID.value, mimeType = "audio/ogg")
setMedia(uri = "someUri", mediaId = AN_EVENT_ID.value, mimeType = MimeTypes.Ogg)
play()
}
val manager = aDefaultRedactedVoiceMessageManager(mediaPlayer = mediaPlayer)
@ -54,7 +55,7 @@ class RedactedVoiceMessageManagerTest {
@Test
fun `redacted event - playing related media is paused`() = runTest {
val mediaPlayer = FakeMediaPlayer().apply {
setMedia(uri = "someUri", mediaId = AN_EVENT_ID.value, mimeType = "audio/ogg")
setMedia(uri = "someUri", mediaId = AN_EVENT_ID.value, mimeType = MimeTypes.Ogg)
play()
}
val manager = aDefaultRedactedVoiceMessageManager(mediaPlayer = mediaPlayer)

View file

@ -29,6 +29,7 @@ import io.element.android.services.analytics.test.FakeAnalyticsService
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import kotlin.time.Duration.Companion.milliseconds
class VoiceMessagePresenterTest {
@Test
@ -49,7 +50,7 @@ class VoiceMessagePresenterTest {
fun `pressing play downloads and plays`() = runTest {
val presenter = createVoiceMessagePresenter(
mediaPlayer = FakeMediaPlayer(fakeTotalDurationMs = 2_000),
content = aTimelineItemVoiceContent(durationMs = 2_000),
content = aTimelineItemVoiceContent(duration = 2_000.milliseconds),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -87,7 +88,7 @@ class VoiceMessagePresenterTest {
mediaPlayer = FakeMediaPlayer(fakeTotalDurationMs = 2_000),
voiceMessageMediaRepo = FakeVoiceMessageMediaRepo().apply { shouldFail = true },
analyticsService = analyticsService,
content = aTimelineItemVoiceContent(durationMs = 2_000),
content = aTimelineItemVoiceContent(duration = 2_000.milliseconds),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -123,7 +124,7 @@ class VoiceMessagePresenterTest {
fun `pressing pause while playing pauses`() = runTest {
val presenter = createVoiceMessagePresenter(
mediaPlayer = FakeMediaPlayer(fakeTotalDurationMs = 2_000),
content = aTimelineItemVoiceContent(durationMs = 2_000),
content = aTimelineItemVoiceContent(duration = 2_000.milliseconds),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -172,7 +173,7 @@ class VoiceMessagePresenterTest {
fun `seeking before play`() = runTest {
val presenter = createVoiceMessagePresenter(
mediaPlayer = FakeMediaPlayer(fakeTotalDurationMs = 2_000),
content = aTimelineItemVoiceContent(durationMs = 10_000),
content = aTimelineItemVoiceContent(duration = 10_000.milliseconds),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -196,7 +197,7 @@ class VoiceMessagePresenterTest {
@Test
fun `seeking after play`() = runTest {
val presenter = createVoiceMessagePresenter(
content = aTimelineItemVoiceContent(durationMs = 10_000),
content = aTimelineItemVoiceContent(duration = 10_000.milliseconds),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()