From d45ffd81813d737459b987aacea86baa3e096dc5 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Sun, 9 Aug 2026 13:47:46 +0300 Subject: [PATCH] Let a voice message without a waveform be seeked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A voice message that arrives without amplitude data — bridged messages and those sent by clients that omit it — is unusable. The waveform is empty, so its measured width is zero: the progress overlay never draws, the cursor is pinned to the left edge, and the touch handler rejects every event because no x coordinate can fall inside a zero-width range. The message plays, but there is no way to see where it is or to move it. Fall back to a flat run of zero amplitudes sized to the canvas when there is no waveform. Width, progress, cursor and seeking then all work exactly as they do for a real waveform, and the existing minimum amplitude renders those zeros as the same flat track a genuinely silent recording already draws, so no new drawing code is needed. Nothing changes for a message that has a waveform, and the model still carries the empty list, which is the honest value. Computing a real waveform from the audio is a separate piece of work. --- .../components/media/WaveformPlaybackView.kt | 6 ++- .../media/WaveformNormalisationTest.kt | 40 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/components/media/WaveformNormalisationTest.kt diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/WaveformPlaybackView.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/WaveformPlaybackView.kt index f91cf96a95..fe9507cb45 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/WaveformPlaybackView.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/WaveformPlaybackView.kt @@ -200,11 +200,15 @@ internal fun WaveformPlaybackViewPreview() = ElementPreview { } } -private fun ImmutableList.normalisedData(maxSamplesCount: Int): ImmutableList { +internal fun ImmutableList.normalisedData(maxSamplesCount: Int): ImmutableList { if (maxSamplesCount <= 0) { return persistentListOf() } + if (isEmpty()) { + return List(maxSamplesCount) { 0f }.toImmutableList() + } + // Filter the data to keep only the expected number of samples val result = if (this.size > maxSamplesCount) { (0..().normalisedData(30) + assertThat(result).hasSize(30) + assertThat(result.toSet()).containsExactly(0f) + } + + @Test + fun `an empty waveform stays empty when there is no room to display it`() { + assertThat(persistentListOf().normalisedData(0)).isEmpty() + assertThat(persistentListOf().normalisedData(-1)).isEmpty() + } + + @Test + fun `a waveform shorter than the display count is kept as it is`() { + val waveform = persistentListOf(0.1f, 0.2f, 0.3f) + assertThat(waveform.normalisedData(30)).containsExactly(0.1f, 0.2f, 0.3f).inOrder() + } + + @Test + fun `a waveform longer than the display count is downsampled`() { + val waveform = persistentListOf(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f) + assertThat(waveform.normalisedData(3)).hasSize(3) + } +}