Display duration of recorded voice message (#1733)
--------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
14dc8e1059
commit
ddc1e1d0cc
12 changed files with 119 additions and 40 deletions
|
|
@ -207,6 +207,7 @@ fun TextComposer(
|
|||
VoiceMessagePreview(
|
||||
isInteractive = !voiceMessageState.isSending,
|
||||
isPlaying = voiceMessageState.isPlaying,
|
||||
showCursor = voiceMessageState.showCursor,
|
||||
waveform = voiceMessageState.waveform,
|
||||
playbackProgress = voiceMessageState.playbackProgress,
|
||||
time = voiceMessageState.time,
|
||||
|
|
@ -816,6 +817,7 @@ internal fun TextComposerVoicePreview() = ElementPreview {
|
|||
voiceMessageState = VoiceMessageState.Preview(
|
||||
isSending = false,
|
||||
isPlaying = false,
|
||||
showCursor = false,
|
||||
waveform = createFakeWaveform(),
|
||||
time = 0.seconds,
|
||||
playbackProgress = 0.0f
|
||||
|
|
@ -826,6 +828,7 @@ internal fun TextComposerVoicePreview() = ElementPreview {
|
|||
voiceMessageState = VoiceMessageState.Preview(
|
||||
isSending = false,
|
||||
isPlaying = true,
|
||||
showCursor = true,
|
||||
waveform = createFakeWaveform(),
|
||||
time = 3.seconds,
|
||||
playbackProgress = 0.2f
|
||||
|
|
@ -836,6 +839,7 @@ internal fun TextComposerVoicePreview() = ElementPreview {
|
|||
voiceMessageState = VoiceMessageState.Preview(
|
||||
isSending = true,
|
||||
isPlaying = false,
|
||||
showCursor = false,
|
||||
waveform = createFakeWaveform(),
|
||||
time = 61.seconds,
|
||||
playbackProgress = 0.0f
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ import kotlin.time.Duration.Companion.seconds
|
|||
internal fun VoiceMessagePreview(
|
||||
isInteractive: Boolean,
|
||||
isPlaying: Boolean,
|
||||
showCursor: Boolean,
|
||||
waveform: ImmutableList<Float>,
|
||||
time: Duration,
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -105,7 +106,7 @@ internal fun VoiceMessagePreview(
|
|||
.weight(1f)
|
||||
.height(26.dp),
|
||||
playbackProgress = playbackProgress,
|
||||
showCursor = isInteractive,
|
||||
showCursor = showCursor,
|
||||
waveform = waveform,
|
||||
seekEnabled = false, // TODO enable seeking
|
||||
onSeek = onSeek,
|
||||
|
|
@ -162,8 +163,29 @@ internal fun VoiceMessagePreviewPreview() = ElementPreview {
|
|||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
VoiceMessagePreview(isInteractive = true, isPlaying = true, time = 2.seconds, playbackProgress = 0.2f, waveform = createFakeWaveform())
|
||||
VoiceMessagePreview(isInteractive = true, isPlaying = false, time = 0.seconds, playbackProgress = 0.0f, waveform = createFakeWaveform())
|
||||
VoiceMessagePreview(isInteractive = false, isPlaying = false, time = 789.seconds, playbackProgress = 0.0f, waveform = createFakeWaveform())
|
||||
VoiceMessagePreview(
|
||||
isInteractive = true,
|
||||
isPlaying = true,
|
||||
time = 2.seconds,
|
||||
playbackProgress = 0.2f,
|
||||
showCursor = true,
|
||||
waveform = createFakeWaveform()
|
||||
)
|
||||
VoiceMessagePreview(
|
||||
isInteractive = true,
|
||||
isPlaying = false,
|
||||
time = 0.seconds,
|
||||
playbackProgress = 0.0f,
|
||||
showCursor = true,
|
||||
waveform = createFakeWaveform()
|
||||
)
|
||||
VoiceMessagePreview(
|
||||
isInteractive = false,
|
||||
isPlaying = false,
|
||||
time = 789.seconds,
|
||||
playbackProgress = 0.0f,
|
||||
showCursor = false,
|
||||
waveform = createFakeWaveform()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ sealed class VoiceMessageState {
|
|||
data class Preview(
|
||||
val isSending: Boolean,
|
||||
val isPlaying: Boolean,
|
||||
val showCursor: Boolean,
|
||||
val playbackProgress: Float,
|
||||
val time: Duration,
|
||||
val waveform: ImmutableList<Float>,
|
||||
|
|
|
|||
|
|
@ -39,10 +39,12 @@ sealed class VoiceRecorderState {
|
|||
* @property file The recorded file.
|
||||
* @property mimeType The mime type of the file.
|
||||
* @property waveform The waveform of the recording.
|
||||
* @property duration The total time spent recording.
|
||||
*/
|
||||
data class Finished(
|
||||
val file: File,
|
||||
val mimeType: String,
|
||||
val waveform: List<Float>,
|
||||
val duration: Duration,
|
||||
) : VoiceRecorderState()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import timber.log.Timber
|
|||
import java.io.File
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
import kotlin.time.TimeSource
|
||||
|
||||
|
|
@ -93,7 +94,7 @@ class VoiceRecorderImpl @Inject constructor(
|
|||
|
||||
val elapsedTime = startedAt.elapsedNow()
|
||||
|
||||
if (elapsedTime >= 30.minutes) {
|
||||
if (elapsedTime > 30.minutes) {
|
||||
Timber.w("Voice message time limit reached")
|
||||
stopRecord(false)
|
||||
return@record
|
||||
|
|
@ -145,11 +146,15 @@ class VoiceRecorderImpl @Inject constructor(
|
|||
_state.emit(
|
||||
when (val file = outputFile) {
|
||||
null -> VoiceRecorderState.Idle
|
||||
else -> VoiceRecorderState.Finished(
|
||||
file = file,
|
||||
mimeType = fileConfig.mimeType,
|
||||
waveform = levels.resample(100),
|
||||
)
|
||||
else -> {
|
||||
val duration = (state.value as? VoiceRecorderState.Recording)?.elapsedTime
|
||||
VoiceRecorderState.Finished(
|
||||
file = file,
|
||||
mimeType = fileConfig.mimeType,
|
||||
waveform = levels.resample(100),
|
||||
duration = duration ?: 0.milliseconds
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import kotlinx.coroutines.test.runTest
|
|||
import org.junit.BeforeClass
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.TestTimeSource
|
||||
|
|
@ -76,34 +77,38 @@ class VoiceRecorderImplTest {
|
|||
|
||||
voiceRecorder.startRecord()
|
||||
assertThat(awaitItem()).isEqualTo(VoiceRecorderState.Recording(0.minutes, listOf(1.0f)))
|
||||
timeSource += 29.minutes
|
||||
assertThat(awaitItem()).isEqualTo(VoiceRecorderState.Recording(29.minutes, listOf()))
|
||||
timeSource += 1.minutes
|
||||
timeSource += 30.minutes
|
||||
assertThat(awaitItem()).isEqualTo(VoiceRecorderState.Recording(30.minutes, listOf()))
|
||||
timeSource += 1.milliseconds
|
||||
|
||||
assertThat(awaitItem()).isEqualTo(
|
||||
VoiceRecorderState.Finished(
|
||||
file = File(FILE_PATH),
|
||||
mimeType = "audio/ogg",
|
||||
waveform = List(100) { 1f },
|
||||
duration = 30.minutes,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when stopped, it provides a file`() = runTest {
|
||||
fun `when stopped, it provides a file and duration`() = runTest {
|
||||
val voiceRecorder = createVoiceRecorder()
|
||||
voiceRecorder.state.test {
|
||||
assertThat(awaitItem()).isEqualTo(VoiceRecorderState.Idle)
|
||||
|
||||
voiceRecorder.startRecord()
|
||||
skipItems(3)
|
||||
skipItems(1)
|
||||
timeSource += 5.seconds
|
||||
skipItems(2)
|
||||
voiceRecorder.stopRecord()
|
||||
assertThat(awaitItem()).isEqualTo(
|
||||
VoiceRecorderState.Finished(
|
||||
file = File(FILE_PATH),
|
||||
mimeType = "audio/ogg",
|
||||
waveform = List(100) { 1f },
|
||||
duration = 5.seconds,
|
||||
)
|
||||
)
|
||||
assertThat(fakeFileSystem.files[File(FILE_PATH)]).isEqualTo(ENCODED_DATA)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ class FakeVoiceRecorder(
|
|||
else -> VoiceRecorderState.Finished(
|
||||
file = curRecording!!,
|
||||
mimeType = "audio/ogg",
|
||||
duration = recordingDuration,
|
||||
waveform = waveform,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue